diff --git a/CMakeLists.txt b/CMakeLists.txt index 1245548..1e1bbbb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,8 +4,42 @@ cmake_minimum_required(VERSION 2.8) include(FindOpenSSL) +############################################################################### +# finding cppunit +############################################################################### +find_path( CPPUNIT_INCLUDE_DIR cppunit/TestFixture.h /usr/include + /usr/local/include + ${CPPUNIT_PREFIX}/include ) +find_library( CPPUNIT_LIBRARY_DEBUG NAMES cppunit cppunit_dll + PATHS /usr/lib + /usr/lib64 + /usr/local/lib + /usr/local/lib64 + ${CPPUNIT_PREFIX}/lib + PATH_SUFFIXES debug ) + +find_library( CPPUNIT_LIBRARY_RELEASE NAMES cppunit cppunit_dll + PATHS /usr/lib + /usr/lib64 + /usr/local/lib + /usr/local/lib64 + ${CPPUNIT_PREFIX}/lib + PATH_SUFFIXES release ) + +set( CPPUNIT_LIBRARY debug ${CPPUNIT_LIBRARY_DEBUG} + optimized ${CPPUNIT_LIBRARY_RELEASE} ) + +if ( CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARY ) + message( STATUS "found cppunit" ) + set( CPPUNIT_FOUND TRUE ) + set( OPT_INCS ${CPPUNIT_INCLUDE_DIR} ) +endif ( CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARY ) + +############################################################################### + include_directories( ${grive_SOURCE_DIR}/src + ${OPT_INCS} ) file(GLOB DRIVE_HEADERS @@ -51,16 +85,20 @@ set_target_properties(grive PROPERTIES SOVERSION 0 VERSION 0.0.1 ) -add_executable( unittest - test/UnitTest.cc - src/util/DateTime.cc - test/util/DateTimeTest.cc - test/util/FunctionTest.cc -) +if ( CPPUNIT_FOUND ) + add_executable( unittest + test/UnitTest.cc + src/util/DateTime.cc + test/util/DateTimeTest.cc + test/util/FunctionTest.cc + ) -target_link_libraries( unittest - cppunit -) + target_link_libraries( unittest + ${CPPUNIT_LIBRARY} + ) + else ( CPPUNIT_FOUND ) + message( STATUS "skip building unittest" ) +endif ( CPPUNIT_FOUND ) ## Install targets diff --git a/src/drive/Drive.cc b/src/drive/Drive.cc index c3f09a9..6d7db36 100644 --- a/src/drive/Drive.cc +++ b/src/drive/Drive.cc @@ -31,6 +31,7 @@ #include #include #include +#include // for debugging only #include @@ -46,8 +47,17 @@ Drive::Drive( OAuth2& auth ) : m_http_hdr.push_back( "Authorization: Bearer " + m_auth.AccessToken() ) ; m_http_hdr.push_back( "GData-Version: 3.0" ) ; - Json resp = Json::Parse( http::Get( root_url + "?alt=json&showfolders=true", m_http_hdr )) ; + + std::cout << http::Get( "https://docs.google.com/feeds/metadata/default", m_http_hdr ) ; + + Json resume_link ; + if ( resp["feed"]["link"].FindInArray( "rel", "http://schemas.google.com/g/2005#resumable-create-media", resume_link ) ) + { + m_resume_link = resume_link["href"].As() ; + std::cout << "resume_link = " << resume_link << std::endl ; + } + Json::Array entries = resp["feed"]["entry"].As() ; ConstructDirTree( entries ) ; @@ -157,7 +167,13 @@ void Drive::UpdateFile( const Json& entry ) std::string filename = entry["docs$filename"]["$t"].Get() ; std::string url = entry["content"]["src"].Get() ; std::string parent_href = Parent( entry ) ; - +/* + Json kind_json ; + if ( entry["category"].FindInArray( "scheme", "http://schemas.google.com/g/2005#kind", kind_json ) ) + { + std::cout << filename << " kind = " << kind_json << std::endl ; + } +*/ bool changed = true ; std::string path = "./" + filename ; @@ -190,7 +206,7 @@ void Drive::UpdateFile( const Json& entry ) DateTime local = ifile ? os::FileMTime( path ) : DateTime() ; // remote file is newer, download file - if ( remote > local ) + if ( !ifile || remote > local ) { std::cout << "downloading " << path << std::endl ; http::GetFile( url, path, m_http_hdr ) ; @@ -199,7 +215,7 @@ std::cout << "downloading " << path << std::endl ; else { std::cout << "local " << filename << " is newer" << std::endl ; -// UploadFile( entry ) ; + UploadFile( entry ) ; } } } @@ -207,15 +223,59 @@ std::cout << "local " << filename << " is newer" << std::endl ; void Drive::UploadFile( const Json& entry ) { -// std::cout << "entry:\n" << entry << std::endl ; - +/* std::string meta = + "" + "" + "" + "Test document" + "" ; +*/ + http::Headers hdr( m_http_hdr ) ; +// hdr.push_back( "Slug: Grive Document" ) ; +// hdr.push_back( "Content-Type: application/atom+xml" ) ; + hdr.push_back( "X-Upload-Content-Type: text/plain" ) ; + hdr.push_back( "X-Upload-Content-Length: 1000" ) ; + hdr.push_back( "Expect:" ) ; +/* + std::string resp = http::PostDataWithHeader( + m_resume_link + "?convert=false", + meta, + hdr ) ; +*/ Json resume_link = entry["link"].FindInArray( "rel", - "http://schemas.google.com/g/2005#resumable-edit-media" )["href"] ; - std::cout << resume_link.As() << std::endl ; + "http://schemas.google.com/g/2005#resumable-edit-media" )["href"] ; + std::cout << resume_link.As() << std::endl ; - std::string resp = http::Put( resume_link.Get(), "", m_http_hdr ) ; + std::string etag = entry["gd$etag"].As() ; + std::cout << "etag = " << etag << std::endl ; + + hdr.push_back( "If-Match: " + etag ) ; + std::string resp = http::Put( resume_link.Get(), "", hdr ) ; + + std::cout << "resp " << resp << std::endl ; + std::istringstream ss( resp ) ; - std::cout << "resp " << resp ; + std::string line ; + while ( std::getline( ss, line ) ) + { + static const std::string location = "Location: " ; + if ( line.substr( 0, location.size() ) == location ) + { + std::string uplink = line.substr( location.size() ) ; + uplink = uplink.substr( 0, uplink.size() -1 ) ; + + std::string data( 1000, 'x' ) ; + http::Headers uphdr ; + uphdr.push_back( "Content-Type: text/plain" ) ; + uphdr.push_back( "Content-Range: bytes 0-999/1000" ) ; + uphdr.push_back( "Expect:" ) ; + uphdr.push_back( "Accept:" ) ; + + std::string resp = http::Put( uplink, data, uphdr ) ; + std::cout << "put response = " << resp << std::endl ; + } + } } } // end of namespace diff --git a/src/drive/Drive.hh b/src/drive/Drive.hh index 768869c..c88113e 100644 --- a/src/drive/Drive.hh +++ b/src/drive/Drive.hh @@ -53,6 +53,8 @@ private : private : OAuth2& m_auth ; std::vector m_http_hdr ; + + std::string m_resume_link ; FolderList m_coll ; Collection m_root ; diff --git a/src/main.cc b/src/main.cc index c542f23..ef14874 100644 --- a/src/main.cc +++ b/src/main.cc @@ -25,6 +25,7 @@ #include #include #include +#include #include #include diff --git a/src/protocol/HTTP.cc b/src/protocol/HTTP.cc index 452fddc..00897fb 100644 --- a/src/protocol/HTTP.cc +++ b/src/protocol/HTTP.cc @@ -51,12 +51,17 @@ size_t ReadCallback( void *ptr, std::size_t size, std::size_t nmemb, std::string assert( ptr != 0 ) ; assert( data != 0 ) ; +std::cout << "reading " << (size*nmemb) << " bytes " << data->size() << std::endl ; + std::size_t count = std::min( size * nmemb, data->size() ) ; if ( count > 0 ) { - std::memcpy( &(*data)[0], ptr, count ) ; + std::memcpy( ptr, &(*data)[0], count ) ; data->erase( 0, count ) ; } + +std::cout << "readed " << count << " bytes " << data->size() << std::endl ; + return count ; } @@ -162,14 +167,41 @@ std::string PostData( const std::string& url, const std::string& data, const Hea std::string post_data = data ; - curl_easy_setopt(curl, CURLOPT_POST, 1); + curl_easy_setopt(curl, CURLOPT_POST, 1L); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, &post_data[0] ) ; curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, post_data.size() ) ; + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1 ) ; DoCurl( curl ) ; return resp; } +std::string PostDataWithHeader( const std::string& url, const std::string& data, const Headers& hdr ) +{ + std::string resp ; + CURL *curl = InitCurl( url, &resp, hdr ) ; + + std::string post_data = data ; + + curl_easy_setopt(curl, CURLOPT_POST, 1L); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, &post_data[0] ) ; + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, post_data.size() ) ; + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L ) ; + curl_easy_setopt(curl, CURLOPT_HEADER, 1L ); + + try + { + DoCurl( curl ) ; + } + catch ( ... ) + { + std::cout << "response = " << resp << std::endl ; + throw ; + } + + return resp; +} + std::string PostFile( const std::string& url, const std::string& filename, const Headers& hdr ) { std::string resp ; @@ -183,18 +215,103 @@ std::string Put( { std::string resp ; CURL *curl = InitCurl( url, &resp, hdr ) ; - - std::string put_data = data ; - curl_easy_setopt(curl, CURLOPT_UPLOAD, 1); + std::string put_data = data ; + + // set common options + curl_easy_setopt(curl, CURLOPT_HEADER, 1L ); + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L ) ; curl_easy_setopt(curl, CURLOPT_READFUNCTION, &ReadCallback ) ; curl_easy_setopt(curl, CURLOPT_READDATA , &put_data ) ; curl_easy_setopt(curl, CURLOPT_INFILESIZE, put_data.size() ) ; - - DoCurl( curl ) ; + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L ) ; + + try + { + DoCurl( curl ) ; + } + catch ( ... ) + { + std::cout << "response = " << resp << std::endl ; + throw ; + } + return resp; } +void Custom( const std::string& url, const std::string& host, const Headers& headers ) +{ + CURL *curl = curl_easy_init(); + if ( curl == 0 ) + throw std::bad_alloc() ; + + // set common options + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L ); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L ) ; + if ( curl_easy_perform( curl ) != 0 ) + throw std::runtime_error( "curl perform fail" ) ; + + long sockextr; + curl_easy_getinfo(curl, CURLINFO_LASTSOCKET, &sockextr); + curl_socket_t sockfd = sockextr ; + + struct timeval tv; + tv.tv_sec = 60 ; + tv.tv_usec = 0 ; + + fd_set infd, outfd, errfd; + FD_ZERO(&infd); + FD_ZERO(&outfd); + FD_ZERO(&errfd); + + FD_SET(sockfd, &errfd); /* always check for error */ + FD_SET(sockfd, &outfd); + if ( select(sockfd + 1, &infd, &outfd, &errfd, &tv) == -1 ) + throw std::runtime_error( "select fail" ) ; + + std::string path = url.substr( host.size() ) ; + std::cout << ("PUT " + path + "HTTP/1.1") << std::endl ; + + std::ostringstream req ; + req << ("PUT " + path + "HTTP/1.1\n") + << "Host: " << "docs.google.com" << "\n" ; + + for ( Headers::const_iterator i = headers.begin() ; i != headers.end() ; ++i ) + req << *i << '\n' ; + + std::string data = "hahaha this is the new file!!!!!" ; + req << "Content-Length: " << data.size() << '\n' + << "Content-Range: 0-" << data.size()-1 << '/' << data.size() << "\n\n\n" + << data ; + + std::string reqstr = req.str() ; + std::cout << "requesting: \n" << reqstr << std::endl ; + + std::size_t iolen ; + if ( curl_easy_send(curl, &reqstr[0], reqstr.size(), &iolen) != CURLE_OK ) + throw std::runtime_error( "cannot send" ) ; + + while ( true ) + { + char buf[1024+1] ; + FD_ZERO(&infd); + FD_ZERO(&outfd); + FD_ZERO(&errfd); + + FD_SET(sockfd, &errfd); /* always check for error */ + FD_SET(sockfd, &infd); + if ( select(sockfd + 1, &infd, &outfd, &errfd, &tv) == -1 ) + throw std::runtime_error( "select fail" ) ; + + if ( curl_easy_recv(curl, buf, 1024, &iolen) != CURLE_OK ) + throw std::runtime_error( "cannot send" ) ; + + buf[iolen] = '\0' ; + std::cout << "read: " << buf << std::endl ; + } +} + std::string Escape( const std::string& str ) { CURL *curl = curl_easy_init(); diff --git a/src/protocol/HTTP.hh b/src/protocol/HTTP.hh index 669d57c..df53a1e 100644 --- a/src/protocol/HTTP.hh +++ b/src/protocol/HTTP.hh @@ -43,6 +43,10 @@ namespace gr { namespace http const std::string& url, const std::string& data, const Headers& hdr = Headers() ) ; + std::string PostDataWithHeader( + const std::string& url, + const std::string& data, + const Headers& hdr = Headers() ) ; std::string PostFile( const std::string& url, const std::string& filename, @@ -53,6 +57,8 @@ namespace gr { namespace http const std::string& data, const Headers& hdr = Headers() ) ; + void Custom( const std::string& url, const std::string& host, const Headers& headers ) ; + std::string Escape( const std::string& str ) ; std::string Unescape( const std::string& str ) ;