diff --git a/grive/src/main.cc b/grive/src/main.cc index d51e0d9..47947f3 100644 --- a/grive/src/main.cc +++ b/grive/src/main.cc @@ -21,6 +21,8 @@ #include "drive/Drive.hh" +#include "http/CurlAgent.hh" +#include "protocol/AuthAgent.hh" #include "protocol/OAuth2.hh" #include "protocol/Json.hh" @@ -172,7 +174,8 @@ int Main( int argc, char **argv ) } OAuth2 token( refresh_token, client_id, client_secret ) ; - Drive drive( token, options ) ; + AuthAgent agent( token, std::auto_ptr( new http::CurlAgent ) ) ; + Drive drive( &agent, options ) ; drive.DetectChanges() ; if ( vm.count( "dry-run" ) == 0 ) diff --git a/libgrive/src/drive/Drive.cc b/libgrive/src/drive/Drive.cc index f0d454f..daf0a72 100644 --- a/libgrive/src/drive/Drive.cc +++ b/libgrive/src/drive/Drive.cc @@ -23,11 +23,11 @@ #include "Entry.hh" #include "Feed.hh" -#include "http/CurlAgent.hh" +#include "http/Agent.hh" #include "http/ResponseLog.hh" #include "http/XmlResponse.hh" #include "protocol/Json.hh" -#include "protocol/OAuth2.hh" +// #include "protocol/OAuth2.hh" #include "util/Destroy.hh" #include "util/log/Log.hh" #include "xml/Node.hh" @@ -53,12 +53,11 @@ namespace const std::string state_file = ".grive_state" ; } -Drive::Drive( OAuth2& auth, const Json& options ) : - m_auth( auth ), +Drive::Drive( http::Agent *http, const Json& options ) : + m_http( http ), m_state( state_file, options ) { - m_http_hdr.Add( "Authorization: Bearer " + m_auth.AccessToken() ) ; - m_http_hdr.Add( "GData-Version: 3.0" ) ; + assert( m_http != 0 ) ; } void Drive::FromRemote( const Entry& entry ) @@ -93,14 +92,14 @@ void Drive::SaveState() m_state.Write( state_file ) ; } -void Drive::SyncFolders( http::Agent *http ) +void Drive::SyncFolders( ) { - assert( http != 0 ) ; + assert( m_http != 0 ) ; Log( "Synchronizing folders", log::info ) ; http::XmlResponse xml ; - http->Get( feed_base + "/-/folder?max-results=50&showroot=true", &xml, m_http_hdr ) ; + m_http->Get( feed_base + "/-/folder?max-results=50&showroot=true", &xml, http::Header() ) ; Feed feed( xml.Response() ) ; do @@ -121,7 +120,7 @@ void Drive::SyncFolders( http::Agent *http ) m_state.FromRemote( e ) ; } } - } while ( feed.GetNext( http, m_http_hdr ) ) ; + } while ( feed.GetNext( m_http, http::Header() ) ) ; m_state.ResolveEntry() ; } @@ -131,17 +130,15 @@ void Drive::DetectChanges() Log( "Reading local directories", log::info ) ; m_state.FromLocal( "." ) ; - http::CurlAgent http ; - long prev_stamp = m_state.ChangeStamp() ; Trace( "previous change stamp is %1%", prev_stamp ) ; - SyncFolders( &http ) ; + SyncFolders( ) ; Log( "Reading remote server file list", log::info ) ; Feed feed ; // feed.EnableLog( "/tmp/file", ".xml" ) ; - feed.Start( &http, m_http_hdr, feed_base + "?showfolders=true&showroot=true" ) ; + feed.Start( m_http, http::Header(), feed_base + "?showfolders=true&showroot=true" ) ; m_resume_link = feed.Root()["link"]. Find( "@rel", "http://schemas.google.com/g/2005#resumable-create-media" )["@href"] ; @@ -152,7 +149,7 @@ void Drive::DetectChanges() feed.begin(), feed.end(), boost::bind( &Drive::FromRemote, this, _1 ) ) ; - } while ( feed.GetNext( &http, m_http_hdr ) ) ; + } while ( feed.GetNext( m_http, http::Header() ) ) ; // pull the changes feed if ( prev_stamp != -1 ) @@ -160,7 +157,7 @@ void Drive::DetectChanges() Log( "Detecting changes from last sync", log::info ) ; Feed changes ; // feed.EnableLog( "/tmp/changes", ".xml" ) ; - feed.Start( &http, m_http_hdr, ChangesFeed(prev_stamp+1) ) ; + feed.Start( m_http, http::Header(), ChangesFeed(prev_stamp+1) ) ; std::for_each( changes.begin(), changes.end(), @@ -171,25 +168,24 @@ void Drive::DetectChanges() void Drive::Update() { Log( "Synchronizing files", log::info ) ; - http::CurlAgent http ; - m_state.Sync( &http, m_http_hdr ) ; + m_state.Sync( m_http ) ; - UpdateChangeStamp( &http ) ; + UpdateChangeStamp( ) ; } void Drive::DryRun() { Log( "Synchronizing files (dry-run)", log::info ) ; - m_state.Sync( 0, m_http_hdr ) ; + m_state.Sync( 0 ) ; } -void Drive::UpdateChangeStamp( http::Agent *http ) +void Drive::UpdateChangeStamp( ) { - assert( http != 0 ) ; + assert( m_http != 0 ) ; // get changed feed http::XmlResponse xrsp ; - http->Get( ChangesFeed(m_state.ChangeStamp()+1), &xrsp, m_http_hdr ) ; + m_http->Get( ChangesFeed(m_state.ChangeStamp()+1), &xrsp, http::Header() ) ; // we should go through the changes to see if it was really Grive to made that change // maybe by recording the updated timestamp and compare it? diff --git a/libgrive/src/drive/Drive.hh b/libgrive/src/drive/Drive.hh index 9a6227c..3641500 100644 --- a/libgrive/src/drive/Drive.hh +++ b/libgrive/src/drive/Drive.hh @@ -35,13 +35,12 @@ namespace http } class Entry ; -class OAuth2 ; class Json ; class Drive { public : - Drive( OAuth2& auth, const Json& options ) ; + Drive( http::Agent *http, const Json& options ) ; void DetectChanges() ; void Update() ; @@ -51,15 +50,16 @@ public : struct Error : virtual Exception {} ; private : - void SyncFolders( http::Agent *http ) ; + void SyncFolders( ) ; void file(); void FromRemote( const Entry& entry ) ; void FromChange( const Entry& entry ) ; - void UpdateChangeStamp( http::Agent *http ) ; + void UpdateChangeStamp( ) ; private : - OAuth2& m_auth ; - http::Header m_http_hdr ; + http::Agent *m_http ; +// OAuth2& m_auth ; +// http::Header m_http_hdr ; std::string m_resume_link ; State m_state ; diff --git a/libgrive/src/drive/Resource.cc b/libgrive/src/drive/Resource.cc index b7e9ffa..268c92f 100644 --- a/libgrive/src/drive/Resource.cc +++ b/libgrive/src/drive/Resource.cc @@ -365,20 +365,20 @@ Resource* Resource::FindChild( const std::string& name ) } // try to change the state to "sync" -void Resource::Sync( http::Agent *http, const http::Header& auth, DateTime& sync_time ) +void Resource::Sync( http::Agent *http, DateTime& sync_time ) { assert( m_state != unknown ) ; assert( !IsRoot() || m_state == sync ) ; // root folder is already synced - SyncSelf( http, auth, sync_time ) ; + SyncSelf( http, sync_time ) ; // if myself is deleted, no need to do the childrens if ( m_state != local_deleted && m_state != remote_deleted ) std::for_each( m_child.begin(), m_child.end(), - boost::bind( &Resource::Sync, _1, http, auth, boost::ref(sync_time) ) ) ; + boost::bind( &Resource::Sync, _1, http, boost::ref(sync_time) ) ) ; } -void Resource::SyncSelf( http::Agent* http, const http::Header& auth, DateTime& sync_time ) +void Resource::SyncSelf( http::Agent* http, DateTime& sync_time ) { assert( !IsRoot() || m_state == sync ) ; // root is always sync assert( IsRoot() || http == 0 || fs::is_directory( m_parent->Path() ) ) ; @@ -392,19 +392,19 @@ void Resource::SyncSelf( http::Agent* http, const http::Header& auth, DateTime& case local_new : Log( "sync %1% doesn't exist in server, uploading", path, log::info ) ; - if ( http != 0 && Create( http, auth, sync_time ) ) + if ( http != 0 && Create( http, sync_time ) ) m_state = sync ; break ; case local_deleted : Log( "sync %1% deleted in local. deleting remote", path, log::info ) ; if ( http != 0 ) - DeleteRemote( http, auth ) ; + DeleteRemote( http ) ; break ; case local_changed : Log( "sync %1% changed in local. uploading", path, log::info ) ; - if ( http != 0 && EditContent( http, auth, sync_time ) ) + if ( http != 0 && EditContent( http, sync_time ) ) m_state = sync ; break ; @@ -415,7 +415,7 @@ void Resource::SyncSelf( http::Agent* http, const http::Header& auth, DateTime& if ( IsFolder() ) fs::create_directories( path ) ; else - Download( http, path, auth ) ; + Download( http, path ) ; m_state = sync ; } @@ -426,7 +426,7 @@ void Resource::SyncSelf( http::Agent* http, const http::Header& auth, DateTime& Log( "sync %1% changed in remote. downloading", path, log::info ) ; if ( http != 0 ) { - Download( http, path, auth ) ; + Download( http, path ) ; m_state = sync ; } break ; @@ -474,14 +474,14 @@ void Resource::DeleteLocal() } } -void Resource::DeleteRemote( http::Agent *http, const http::Header& auth ) +void Resource::DeleteRemote( http::Agent *http ) { assert( http != 0 ) ; http::StringResponse str ; try { - http::Header hdr( auth ) ; + http::Header hdr ; hdr.Add( "If-Match: " + m_etag ) ; // doesn't know why, but an update before deleting seems to work always @@ -502,12 +502,12 @@ void Resource::DeleteRemote( http::Agent *http, const http::Header& auth ) } -void Resource::Download( http::Agent* http, const fs::path& file, const http::Header& auth ) const +void Resource::Download( http::Agent* http, const fs::path& file ) const { assert( http != 0 ) ; http::Download dl( file.string(), http::Download::NoChecksum() ) ; - long r = http->Get( m_content, &dl, auth ) ; + long r = http->Get( m_content, &dl, http::Header() ) ; if ( r <= 400 ) { if ( m_mtime != DateTime() ) @@ -517,7 +517,7 @@ void Resource::Download( http::Agent* http, const fs::path& file, const http::He } } -bool Resource::EditContent( http::Agent* http, const http::Header& auth, DateTime& sync_time ) +bool Resource::EditContent( http::Agent* http, DateTime& sync_time ) { assert( http != 0 ) ; assert( m_parent != 0 ) ; @@ -530,10 +530,10 @@ bool Resource::EditContent( http::Agent* http, const http::Header& auth, DateTim return false ; } - return Upload( http, m_edit, auth, false, sync_time ) ; + return Upload( http, m_edit, false, sync_time ) ; } -bool Resource::Create( http::Agent* http, const http::Header& auth, DateTime& sync_time ) +bool Resource::Create( http::Agent* http, DateTime& sync_time ) { assert( http != 0 ) ; assert( m_parent != 0 ) ; @@ -551,7 +551,7 @@ bool Resource::Create( http::Agent* http, const http::Header& auth, DateTime& sy % xml::Escape(m_name) ).str() ; - http::Header hdr( auth ) ; + http::Header hdr ; hdr.Add( "Content-Type: application/atom+xml" ) ; http::XmlResponse xml ; @@ -563,7 +563,7 @@ bool Resource::Create( http::Agent* http, const http::Header& auth, DateTime& sy } else if ( !m_parent->m_create.empty() ) { - return Upload( http, m_parent->m_create + "?convert=false", auth, true, sync_time ) ; + return Upload( http, m_parent->m_create + "?convert=false", true, sync_time ) ; } else { @@ -575,7 +575,6 @@ bool Resource::Create( http::Agent* http, const http::Header& auth, DateTime& sy bool Resource::Upload( http::Agent* http, const std::string& link, - const http::Header& auth, bool post, DateTime& sync_time ) { @@ -585,7 +584,7 @@ bool Resource::Upload( std::ostringstream xcontent_len ; xcontent_len << "X-Upload-Content-Length: " << file.Size() ; - http::Header hdr( auth ) ; + http::Header hdr ; hdr.Add( "Content-Type: application/atom+xml" ) ; hdr.Add( "X-Upload-Content-Type: application/octet-stream" ) ; hdr.Add( xcontent_len.str() ) ; diff --git a/libgrive/src/drive/Resource.hh b/libgrive/src/drive/Resource.hh index 8a76017..75efdc4 100644 --- a/libgrive/src/drive/Resource.hh +++ b/libgrive/src/drive/Resource.hh @@ -32,7 +32,6 @@ namespace gr { namespace http { class Agent ; - class Header ; } class Entry ; @@ -77,7 +76,7 @@ public : void FromRemote( const Entry& remote, const DateTime& last_sync ) ; void FromLocal( const DateTime& last_sync ) ; - void Sync( http::Agent* http, const http::Header& auth, DateTime& sync_time ) ; + void Sync( http::Agent* http, DateTime& sync_time ) ; // children access iterator begin() const ; @@ -124,19 +123,19 @@ private : private : void SetState( State new_state ) ; - void Download( http::Agent* http, const fs::path& file, const http::Header& auth ) const ; - bool EditContent( http::Agent* http, const http::Header& auth, DateTime& sync_time ) ; - bool Create( http::Agent* http, const http::Header& auth, DateTime& sync_time ) ; - bool Upload( http::Agent* http, const std::string& link, const http::Header& auth, bool post, DateTime& sync_time ) ; + void Download( http::Agent* http, const fs::path& file ) const ; + bool EditContent( http::Agent* http, DateTime& sync_time ) ; + bool Create( http::Agent* http, DateTime& sync_time ) ; + bool Upload( http::Agent* http, const std::string& link, bool post, DateTime& sync_time ) ; void FromRemoteFolder( const Entry& remote, const DateTime& last_sync ) ; void FromRemoteFile( const Entry& remote, const DateTime& last_sync ) ; void DeleteLocal() ; - void DeleteRemote( http::Agent* http, const http::Header& auth ) ; + void DeleteRemote( http::Agent* http ) ; void AssignIDs( const Entry& remote ) ; - void SyncSelf( http::Agent* http, const http::Header& auth, DateTime& sync_time ) ; + void SyncSelf( http::Agent* http, DateTime& sync_time ) ; private : std::string m_name ; diff --git a/libgrive/src/drive/State.cc b/libgrive/src/drive/State.cc index f024818..afab842 100644 --- a/libgrive/src/drive/State.cc +++ b/libgrive/src/drive/State.cc @@ -24,7 +24,6 @@ #include "CommonUri.hh" #include "http/Agent.hh" -#include "http/Header.hh" #include "util/Crypt.hh" #include "util/log/Log.hh" #include "protocol/Json.hh" @@ -262,7 +261,7 @@ void State::Write( const fs::path& filename ) const fs << result ; } -void State::Sync( http::Agent *http, const http::Header& auth ) +void State::Sync( http::Agent *http ) { // set the last sync time from the time returned by the server for the last file synced // if the sync time hasn't changed (i.e. now files have been uploaded) @@ -272,7 +271,7 @@ void State::Sync( http::Agent *http, const http::Header& auth ) // TODO - WARNING - do we use the last sync time to compare to client file times // need to check if this introduces a new problem DateTime last_sync_time = m_last_sync; - m_res.Root()->Sync( http, auth, last_sync_time ) ; + m_res.Root()->Sync( http, last_sync_time ) ; if ( last_sync_time == m_last_sync ) { Trace( "nothing changed? %1%", m_last_sync ) ; diff --git a/libgrive/src/drive/State.hh b/libgrive/src/drive/State.hh index 563abe5..cd0590d 100644 --- a/libgrive/src/drive/State.hh +++ b/libgrive/src/drive/State.hh @@ -31,7 +31,6 @@ namespace gr { namespace http { class Agent ; - class Header ; } class Json ; @@ -58,7 +57,7 @@ public : Resource* FindByID( const std::string& id ) ; Resource* Find( const fs::path& path ) ; - void Sync( http::Agent *http, const http::Header& auth ) ; + void Sync( http::Agent *http ) ; iterator begin() ; iterator end() ; diff --git a/libgrive/src/http/Header.cc b/libgrive/src/http/Header.cc index 36bd245..9f0bdd3 100644 --- a/libgrive/src/http/Header.cc +++ b/libgrive/src/http/Header.cc @@ -50,4 +50,11 @@ std::ostream& operator<<( std::ostream& os, const Header& h ) return os ; } +Header operator+( const Header& header, const std::string& str ) +{ + Header h( header ) ; + h.Add( str ) ; + return h ; +} + } } // end of namespace diff --git a/libgrive/src/http/Header.hh b/libgrive/src/http/Header.hh index c581d7d..2486cd0 100644 --- a/libgrive/src/http/Header.hh +++ b/libgrive/src/http/Header.hh @@ -46,5 +46,6 @@ private : } ; std::ostream& operator<<( std::ostream& os, const Header& h ) ; +Header operator+( const Header& header, const std::string& str ) ; }} // end of namespace diff --git a/libgrive/src/protocol/AuthAgent.cc b/libgrive/src/protocol/AuthAgent.cc new file mode 100644 index 0000000..06cacd2 --- /dev/null +++ b/libgrive/src/protocol/AuthAgent.cc @@ -0,0 +1,103 @@ +/* + 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 "AuthAgent.hh" +#include "http/Header.hh" + +#include + +namespace gr { + +using namespace http ; + +AuthAgent::AuthAgent( const OAuth2& auth, std::auto_ptr real_agent ) : + m_auth ( auth ), + m_agent ( real_agent ) +{ + assert( m_agent.get() != 0 ) ; +} + +Header AuthAgent::AppendHeader( const Header& hdr ) const +{ + Header h(hdr) ; + h.Add( "Authorization: Bearer " + m_auth.AccessToken() ) ; + h.Add( "GData-Version: 3.0" ) ; + return h ; +} + +long AuthAgent::Put( + const std::string& url, + const std::string& data, + Receivable *dest, + const Header& hdr ) +{ + return m_agent->Put( url, data, dest, AppendHeader(hdr) ) ; +} + +long AuthAgent::Put( + const std::string& url, + StdioFile& file, + Receivable *dest, + const Header& hdr ) +{ + return m_agent->Put( url, file, dest, AppendHeader(hdr) ) ; +} + +long AuthAgent::Get( + const std::string& url, + Receivable *dest, + const Header& hdr ) +{ + return m_agent->Get( url, dest, AppendHeader(hdr) ) ; +} + +long AuthAgent::Post( + const std::string& url, + const std::string& data, + Receivable *dest, + const Header& hdr ) +{ + return m_agent->Post( url, data, dest, AppendHeader(hdr) ) ; +} + +long AuthAgent::Custom( + const std::string& method, + const std::string& url, + Receivable *dest, + const Header& hdr ) +{ + return m_agent->Custom( method, url, dest, AppendHeader(hdr) ) ; +} + +std::string AuthAgent::RedirLocation() const +{ + return m_agent->RedirLocation() ; +} + +std::string AuthAgent::Escape( const std::string& str ) +{ + return m_agent->Escape( str ) ; +} + +std::string AuthAgent::Unescape( const std::string& str ) +{ + return m_agent->Unescape( str ) ; +} + +} // end of namespace diff --git a/libgrive/src/protocol/AuthAgent.hh b/libgrive/src/protocol/AuthAgent.hh new file mode 100644 index 0000000..d3fb053 --- /dev/null +++ b/libgrive/src/protocol/AuthAgent.hh @@ -0,0 +1,81 @@ +/* + 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. +*/ + +#pragma once + +#include "http/Agent.hh" +#include "OAuth2.hh" + +#include + +namespace gr { + +/*! \brief An HTTP agent with support OAuth2 + + This is a HTTP agent that provide support for OAuth2. It will also perform retries on + certain HTTP errors. +*/ +class AuthAgent : public http::Agent +{ +public : + AuthAgent( const OAuth2& auth, std::auto_ptr real_agent ) ; + + long Put( + const std::string& url, + const std::string& data, + http::Receivable *dest, + const http::Header& hdr ) ; + + long Put( + const std::string& url, + StdioFile& file, + http::Receivable *dest, + const http::Header& hdr ) ; + + long Get( + const std::string& url, + http::Receivable *dest, + const http::Header& hdr ) ; + + long Post( + const std::string& url, + const std::string& data, + http::Receivable *dest, + const http::Header& hdr ) ; + + long Custom( + const std::string& method, + const std::string& url, + http::Receivable *dest, + const http::Header& hdr ) ; + + std::string RedirLocation() const ; + + std::string Escape( const std::string& str ) ; + std::string Unescape( const std::string& str ) ; + +private : + http::Header AppendHeader( const http::Header& hdr ) const ; + +private : + OAuth2 m_auth ; + const std::auto_ptr m_agent ; +} ; + +} // end of namespace