/* grive: an GPL program to sync a local directory with Google Drive Copyright (C) 2012 Wan Wai Ho This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "util/Config.hh" #include "util/ProgressBar.hh" #include "base/Drive.hh" #include "drive2/Syncer2.hh" #include "http/CurlAgent.hh" #include "protocol/AuthAgent.hh" #include "protocol/OAuth2.hh" #include "json/Val.hh" #include "bfd/Backtrace.hh" #include "util/Exception.hh" #include "util/log/Log.hh" #include "util/log/CompositeLog.hh" #include "util/log/DefaultLog.hh" // boost header #include #include // initializing libgcrypt, must be done in executable #include #include #include #include #include const std::string default_id = APP_ID ; const std::string default_secret = APP_SECRET ; using namespace gr ; namespace po = boost::program_options; // libgcrypt insist this to be done in application, not library void InitGCrypt() { if ( !gcry_check_version(GCRYPT_VERSION) ) throw std::runtime_error( "libgcrypt version mismatch" ) ; // disable secure memory gcry_control(GCRYCTL_DISABLE_SECMEM, 0); // tell Libgcrypt that initialization has completed gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); } void InitLog( const po::variables_map& vm ) { std::unique_ptr comp_log( new log::CompositeLog ) ; std::unique_ptr def_log( new log::DefaultLog ); LogBase* console_log = comp_log->Add( def_log ) ; if ( vm.count( "log" ) ) { std::unique_ptr file_log( new log::DefaultLog( vm["log"].as() ) ) ; file_log->Enable( log::debug ) ; file_log->Enable( log::verbose ) ; file_log->Enable( log::info ) ; file_log->Enable( log::warning ) ; file_log->Enable( log::error ) ; file_log->Enable( log::critical ) ; // log grive version to log file file_log->Log( log::Fmt("grive version " VERSION " " __DATE__ " " __TIME__), log::verbose ) ; file_log->Log( log::Fmt("current time: %1%") % DateTime::Now(), log::verbose ) ; comp_log->Add( file_log ) ; } if ( vm.count( "verbose" ) ) { console_log->Enable( log::verbose ) ; } if ( vm.count( "debug" ) ) { console_log->Enable( log::verbose ) ; console_log->Enable( log::debug ) ; } LogBase::Inst( comp_log.release() ) ; } int Main( int argc, char **argv ) { InitGCrypt() ; // construct the program options po::options_description desc( "Grive options" ); desc.add_options() ( "help,h", "Produce help message" ) ( "version,v", "Display Grive version" ) ( "auth,a", "Request authorization token" ) ( "id,i", po::value(), "Authentication ID") ( "secret,e", po::value(), "Authentication secret") ( "print-url", "Only print url for request") ( "path,p", po::value(), "Path to working copy root") ( "dir,s", po::value(), "Single subdirectory to sync") ( "verbose,V", "Verbose mode. Enable more messages than normal.") ( "log-http", po::value(), "Log all HTTP responses in this file for debugging.") ( "new-rev", "Create new revisions in server for updated files.") ( "debug,d", "Enable debug level messages. Implies -v.") ( "log,l", po::value(), "Set log output filename." ) ( "force,f", "Force grive to always download a file from Google Drive " "instead of uploading it." ) ( "upload-only,u", "Do not download anything from Google Drive, only upload local changes" ) ( "no-remote-new,n", "Download only files that are changed in Google Drive and already exist locally" ) ( "dry-run", "Only detect which files need to be uploaded/downloaded, " "without actually performing them." ) ( "upload-speed,U", po::value(), "Limit upload speed in kbytes per second" ) ( "download-speed,D", po::value(), "Limit download speed in kbytes per second" ) ( "progress-bar,P", "Enable progress bar for upload/download of files") ; po::variables_map vm; try { po::store( po::parse_command_line( argc, argv, desc ), vm ); } catch( po::error &e ) { std::cerr << "Options are incorrect. Use -h for help\n"; return -1; } po::notify( vm ); // simple commands that doesn't require log or config if ( vm.count("help") ) { std::cout << desc << std::endl ; return 0 ; } else if ( vm.count( "version" ) ) { std::cout << "grive version " << VERSION << ' ' << __DATE__ << ' ' << __TIME__ << std::endl ; return 0 ; } // initialize logging InitLog( vm ) ; Config config( vm ) ; Log( "config file name %1%", config.Filename(), log::verbose ); std::unique_ptr http( new http::CurlAgent ); if ( vm.count( "log-http" ) ) http->SetLog( new http::ResponseLog( vm["log-http"].as(), ".txt" ) ); std::unique_ptr pb; if ( vm.count( "progress-bar" ) ) { pb.reset( new ProgressBar() ); http->SetProgressReporter( pb.get() ); } if ( vm.count( "auth" ) ) { std::string id = vm.count( "id" ) > 0 ? vm["id"].as() : default_id ; std::string secret = vm.count( "secret" ) > 0 ? vm["secret"].as() : default_secret ; OAuth2 token( http.get(), id, secret ) ; if ( vm.count("print-url") ) { std::cout << token.MakeAuthURL() << std::endl ; return 0 ; } std::cout << "-----------------------\n" << "Please open this URL in your browser to authenticate Grive2:\n\n" << token.MakeAuthURL() << std::endl ; if ( !token.GetCode() ) { std::cout << "Authentication failed\n"; return -1; } // save to config config.Set( "id", Val( id ) ) ; config.Set( "secret", Val( secret ) ) ; config.Set( "refresh_token", Val( token.RefreshToken() ) ) ; config.Save() ; } std::string refresh_token ; std::string id ; std::string secret ; try { refresh_token = config.Get("refresh_token").Str() ; id = config.Get("id").Str() ; secret = config.Get("secret").Str() ; } catch ( Exception& e ) { Log( "Please run grive with the \"-a\" option if this is the " "first time you're accessing your Google Drive!", log::critical ) ; return -1; } OAuth2 token( http.get(), refresh_token, id, secret ) ; AuthAgent agent( token, http.get() ) ; v2::Syncer2 syncer( &agent ); if ( vm.count( "upload-speed" ) > 0 ) agent.SetUploadSpeed( vm["upload-speed"].as() * 1000 ); if ( vm.count( "download-speed" ) > 0 ) agent.SetDownloadSpeed( vm["download-speed"].as() * 1000 ); Drive drive( &syncer, config.GetAll() ) ; drive.DetectChanges() ; if ( vm.count( "dry-run" ) == 0 ) { // The progress bar should just be enabled when actual file transfers take place if ( pb ) pb->setShowProgressBar( true ) ; drive.Update() ; if ( pb ) pb->setShowProgressBar( false ) ; drive.SaveState() ; } else drive.DryRun() ; config.Save() ; Log( "Finished!", log::info ) ; return 0 ; } int main( int argc, char **argv ) { try { return Main( argc, argv ) ; } catch ( Exception& e ) { Log( "exception: %1%", boost::diagnostic_information(e), log::critical ) ; } catch ( std::exception& e ) { Log( "exception: %1%", e.what(), log::critical ) ; } catch ( ... ) { Log( "unexpected exception", log::critical ) ; } return -1 ; }