diff --git a/grive/src/main.cc b/grive/src/main.cc index ea10aaa..175d661 100644 --- a/grive/src/main.cc +++ b/grive/src/main.cc @@ -163,6 +163,8 @@ int main( int argc, char **argv ) { OAuth2 token( refresh_token, client_id, client_secret ) ; Drive drive( token ) ; + + drive.Update() ; } catch ( gr::Exception& e ) { diff --git a/libgrive/src/drive/Collection.cc b/libgrive/src/drive/Collection.cc index 88bfe15..0166770 100644 --- a/libgrive/src/drive/Collection.cc +++ b/libgrive/src/drive/Collection.cc @@ -87,9 +87,9 @@ void Collection::AddChild( Collection *child ) m_child.push_back( child ) ; } -void Collection::AddLeaf( const std::string& filename ) +void Collection::AddLeaf( File *file ) { - m_leaves.push_back( filename ) ; + m_leaf.push_back( file ) ; } void Collection::Swap( Collection& coll ) @@ -97,7 +97,7 @@ void Collection::Swap( Collection& coll ) m_entry.Swap( coll.m_entry ) ; std::swap( m_parent, coll.m_parent ) ; m_child.swap( coll.m_child ) ; - m_leaves.swap( coll.m_leaves ) ; + m_leaf.swap( coll.m_leaf ) ; } void Collection::CreateSubDir( const Path& prefix ) diff --git a/libgrive/src/drive/Collection.hh b/libgrive/src/drive/Collection.hh index 723c38e..ae17295 100644 --- a/libgrive/src/drive/Collection.hh +++ b/libgrive/src/drive/Collection.hh @@ -29,6 +29,7 @@ namespace gr { class Path ; +class File ; class Collection { @@ -48,7 +49,7 @@ public : bool IsInRootTree() const ; void AddChild( Collection *child ) ; - void AddLeaf( const std::string& filename ) ; + void AddLeaf( File *file ) ; void Swap( Collection& coll ) ; @@ -66,8 +67,7 @@ private : // not owned Collection *m_parent ; std::vector m_child ; - - std::vector m_leaves ; + std::vector m_leaf ; } ; } // end of namespace diff --git a/libgrive/src/drive/Drive.cc b/libgrive/src/drive/Drive.cc index 79b5505..e5b14ef 100644 --- a/libgrive/src/drive/Drive.cc +++ b/libgrive/src/drive/Drive.cc @@ -21,19 +21,20 @@ #include "CommonUri.hh" #include "Entry.hh" +#include "File.hh" #include "http/Agent.hh" #include "http/ResponseLog.hh" #include "http/XmlResponse.hh" #include "protocol/OAuth2.hh" -#include "util/Crypt.hh" -#include "util/DateTime.hh" +#include "util/Destroy.hh" #include "util/Log.hh" -#include "util/OS.hh" #include "util/Path.hh" #include "xml/Node.hh" #include "xml/NodeSet.hh" +#include + // standard C++ library #include #include @@ -95,6 +96,7 @@ Drive::Drive( OAuth2& auth ) : Drive::~Drive( ) { + std::for_each( m_files.begin(), m_files.end(), Destroy() ) ; } struct SortCollectionByHref @@ -184,41 +186,28 @@ void Drive::ConstructDirTree( http::Agent *http ) Root()->CreateSubDir( Path() ) ; } -void Drive::UpdateFile( Entry& file, const Collection& parent, http::Agent *http ) +void Drive::UpdateFile( Entry& entry, Collection& parent, http::Agent *http ) { // only handle uploaded files - if ( !file.Filename().empty() ) + if ( !entry.Filename().empty() ) { - bool changed = true ; - Path path = parent.Dir() / file.Filename() ; + File *file = new File( entry, &parent ) ; + m_files.push_back( file ) ; + parent.AddLeaf( file ) ; - // compare checksum first if file exists - std::ifstream ifile( path.Str().c_str(), std::ios::binary | std::ios::in ) ; - if ( ifile && file.ServerMD5() == crypt::MD5(ifile.rdbuf()) ) - changed = false ; - - // if the checksum is different, file is changed and we need to update - if ( changed ) - { - DateTime remote = file.ServerModified() ; - DateTime local = ifile ? os::FileMTime( path ) : DateTime() ; - - // remote file is newer, download file - if ( !ifile || remote > local ) - file.Download( http, path, m_http_hdr ) ; - - else - { - // re-reading the file - ifile.seekg(0) ; - file.Upload( http, ifile.rdbuf(), m_http_hdr ) ; - } - } +// file->Update( http, m_http_hdr ) ; } else { - Log( "file \"%1%\" is a google document, ignored", file.Title() ) ; + Log( "file \"%1%\" is a google document, ignored", entry.Title() ) ; } } +void Drive::Update() +{ + http::Agent http ; + std::for_each( m_files.begin(), m_files.end(), + boost::bind( &File::Update, _1, &http, m_http_hdr ) ) ; +} + } // end of namespace diff --git a/libgrive/src/drive/Drive.hh b/libgrive/src/drive/Drive.hh index 4d62d59..81f6768 100644 --- a/libgrive/src/drive/Drive.hh +++ b/libgrive/src/drive/Drive.hh @@ -37,7 +37,7 @@ namespace xml } class OAuth2 ; -class Json ; +class File ; class Drive { @@ -49,10 +49,12 @@ public : Drive( OAuth2& auth ) ; ~Drive( ) ; + void Update() ; + struct Error : virtual Exception {} ; private : - void UpdateFile( Entry& file, const Collection& parent, http::Agent *http ) ; + void UpdateFile( Entry& file, Collection& parent, http::Agent *http ) ; void ConstructDirTree( http::Agent *http ) ; @@ -66,6 +68,7 @@ private : std::string m_resume_link ; FolderList m_coll ; + std::vector m_files ; } ; } // end of namespace diff --git a/libgrive/src/drive/Entry.cc b/libgrive/src/drive/Entry.cc index da87106..f8634f4 100644 --- a/libgrive/src/drive/Entry.cc +++ b/libgrive/src/drive/Entry.cc @@ -19,12 +19,6 @@ #include "Entry.hh" -#include "CommonUri.hh" - -#include "http/Download.hh" -#include "http/StringResponse.hh" -#include "http/XmlResponse.hh" -#include "protocol/OAuth2.hh" #include "util/Log.hh" #include "util/OS.hh" #include "util/Path.hh" @@ -33,7 +27,6 @@ #include #include -#include namespace gr { @@ -122,66 +115,6 @@ std::string Entry::ParentHref() const return m_parent_href ; } -void Entry::Download( http::Agent* http, const Path& file, const http::Headers& auth ) const -{ - Log( "Downloading %1%", file ) ; - http::Download dl( file.Str(), http::Download::NoChecksum() ) ; - long r = http->Get( m_content_src, &dl, auth ) ; - if ( r <= 400 ) - os::SetFileTime( file, m_server_modified ) ; -} - -bool Entry::Upload( http::Agent* http, std::streambuf *file, const http::Headers& auth ) -{ - // upload link missing means that file is read only - if ( m_upload_link.empty() ) - { - Log( "Cannot upload %1%: file read-only.", m_title, log::warning ) ; - return false ; - } - - Log( "Uploading %1%", m_title ) ; - - std::string meta = - "\n" - "" - "" - "" + m_filename + "" - "" ; - - std::string data( - (std::istreambuf_iterator(file)), - (std::istreambuf_iterator()) ) ; - - std::ostringstream xcontent_len ; - xcontent_len << "X-Upload-Content-Length: " << data.size() ; - - http::Headers hdr( auth ) ; - hdr.push_back( "Content-Type: application/atom+xml" ) ; - hdr.push_back( "X-Upload-Content-Type: application/octet-stream" ) ; - hdr.push_back( xcontent_len.str() ) ; - hdr.push_back( "If-Match: " + m_etag ) ; - hdr.push_back( "Expect:" ) ; - - http::StringResponse str ; - http->Put( m_upload_link, meta, &str, hdr ) ; - - std::string uplink = http->RedirLocation() ; - - // parse the header and find "Location" - http::Headers uphdr ; - uphdr.push_back( "Expect:" ) ; - uphdr.push_back( "Accept:" ) ; - - http::XmlResponse xml ; - http->Put( uplink, data, &xml, uphdr ) ; - - Trace( "Receipted response = %1%", xml.Response() ) ; - - return true ; -} - std::string Entry::ResourceID() const { return m_resource_id ; @@ -192,13 +125,14 @@ std::string Entry::ETag() const return m_etag ; } -void Entry::Delete( http::Agent *http, const http::Headers& auth ) +std::string Entry::ContentSrc() const { - http::Headers hdr( auth ) ; - hdr.push_back( "If-Match: " + m_etag ) ; - - http::StringResponse str ; - http->Custom( "DELETE", feed_base + "/" + m_resource_id + "?delete=true", &str, hdr ) ; + return m_content_src ; +} + +std::string Entry::UploadLink() const +{ + return m_upload_link ; } void Entry::Swap( Entry& e ) diff --git a/libgrive/src/drive/Entry.hh b/libgrive/src/drive/Entry.hh index 8251a7e..29bfaec 100644 --- a/libgrive/src/drive/Entry.hh +++ b/libgrive/src/drive/Entry.hh @@ -19,12 +19,11 @@ #pragma once -#include - -#include "http/Agent.hh" #include "util/DateTime.hh" #include +#include +#include namespace gr { @@ -33,8 +32,6 @@ namespace xml class Node ; } -class Json ; -class OAuth2 ; class Path ; /*! \brief corresponds to an "entry" in the resource feed @@ -60,13 +57,11 @@ public : std::string SelfHref() const ; std::string ParentHref() const ; + std::string ContentSrc() const ; + std::string UploadLink() const ; const std::vector& ParentHrefs() const ; - void Download( http::Agent* http, const Path& file, const http::Headers& auth ) const ; - bool Upload( http::Agent* http, std::streambuf *file, const http::Headers& auth ) ; - void Delete( http::Agent* http, const gr::http::Headers& auth ) ; - void Swap( Entry& e ) ; private : diff --git a/libgrive/src/drive/File.cc b/libgrive/src/drive/File.cc new file mode 100644 index 0000000..153b23e --- /dev/null +++ b/libgrive/src/drive/File.cc @@ -0,0 +1,148 @@ +/* + 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 "File.hh" + +#include "Collection.hh" +#include "CommonUri.hh" + +#include "http/Download.hh" +#include "http/StringResponse.hh" +#include "http/XmlResponse.hh" +#include "protocol/OAuth2.hh" +#include "util/Crypt.hh" +#include "util/DateTime.hh" +#include "util/Path.hh" +#include "util/OS.hh" +#include "util/Log.hh" +#include "xml/Node.hh" + +#include +#include + +namespace gr { + +File::File( const Entry& entry, const Collection *parent ) : + m_entry ( entry ), + m_parent ( parent ) +{ +} + +void File::Update( http::Agent *http, const http::Headers& auth ) +{ + assert( m_parent != 0 ) ; + + bool changed = true ; + Path path = m_parent->Dir() / m_entry.Filename() ; + + // compare checksum first if file exists + std::ifstream ifile( path.Str().c_str(), std::ios::binary | std::ios::in ) ; + if ( ifile && m_entry.ServerMD5() == crypt::MD5(ifile.rdbuf()) ) + changed = false ; + + // if the checksum is different, file is changed and we need to update + if ( changed ) + { + DateTime remote = m_entry.ServerModified() ; + DateTime local = ifile ? os::FileMTime( path ) : DateTime() ; + + // remote file is newer, download file + if ( !ifile || remote > local ) + Download( http, path, auth ) ; + + else + { + // re-reading the file + ifile.seekg(0) ; + Upload( http, ifile.rdbuf(), auth ) ; + } + } +} + +void File::Delete( http::Agent *http, const http::Headers& auth ) +{ + http::Headers hdr( auth ) ; + hdr.push_back( "If-Match: " + m_entry.ETag() ) ; + + http::StringResponse str ; + http->Custom( "DELETE", feed_base + "/" + m_entry.ResourceID() + "?delete=true", &str, hdr ) ; +} + + +void File::Download( http::Agent* http, const Path& file, const http::Headers& auth ) const +{ + Log( "Downloading %1%", file ) ; + http::Download dl( file.Str(), http::Download::NoChecksum() ) ; + long r = http->Get( m_entry.ContentSrc(), &dl, auth ) ; + if ( r <= 400 ) + os::SetFileTime( file, m_entry.ServerModified() ) ; +} + +bool File::Upload( http::Agent* http, std::streambuf *file, const http::Headers& auth ) +{ + // upload link missing means that file is read only + if ( m_entry.UploadLink().empty() ) + { + Log( "Cannot upload %1%: file read-only.", m_entry.Title(), log::warning ) ; + return false ; + } + + Log( "Uploading %1%", m_entry.Title() ) ; + + std::string meta = + "\n" + "" + "" + "" + m_entry.Filename() + "" + "" ; + + std::string data( + (std::istreambuf_iterator(file)), + (std::istreambuf_iterator()) ) ; + + std::ostringstream xcontent_len ; + xcontent_len << "X-Upload-Content-Length: " << data.size() ; + + http::Headers hdr( auth ) ; + hdr.push_back( "Content-Type: application/atom+xml" ) ; + hdr.push_back( "X-Upload-Content-Type: application/octet-stream" ) ; + hdr.push_back( xcontent_len.str() ) ; + hdr.push_back( "If-Match: " + m_entry.ETag() ) ; + hdr.push_back( "Expect:" ) ; + + http::StringResponse str ; + http->Put( m_entry.UploadLink(), meta, &str, hdr ) ; + + std::string uplink = http->RedirLocation() ; + + // parse the header and find "Location" + http::Headers uphdr ; + uphdr.push_back( "Expect:" ) ; + uphdr.push_back( "Accept:" ) ; + + http::XmlResponse xml ; + http->Put( uplink, data, &xml, uphdr ) ; + + Trace( "Receipted response = %1%", xml.Response() ) ; + + return true ; +} + +} // end of namespace diff --git a/libgrive/src/drive/File.hh b/libgrive/src/drive/File.hh new file mode 100644 index 0000000..ec37ea5 --- /dev/null +++ b/libgrive/src/drive/File.hh @@ -0,0 +1,52 @@ +/* + 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 "Entry.hh" +#include "http/Agent.hh" + +#include + +namespace gr { + +namespace http +{ + class Agent ; +} + +class Collection ; + +class File +{ +public : + explicit File( const Entry& e, const Collection *parent ) ; + + void Update( http::Agent *http, const http::Headers& auth ) ; + + void Download( http::Agent* http, const Path& file, const http::Headers& auth ) const ; + bool Upload( http::Agent* http, std::streambuf *file, const http::Headers& auth ) ; + void Delete( http::Agent* http, const http::Headers& auth ) ; + +private : + Entry m_entry ; + const Collection *m_parent ; +} ; + +} // end of namespace diff --git a/libgrive/src/http/Agent.cc b/libgrive/src/http/Agent.cc index aa0a9e3..0d09efd 100644 --- a/libgrive/src/http/Agent.cc +++ b/libgrive/src/http/Agent.cc @@ -135,7 +135,7 @@ long Agent::ExecCurl( long http_code = 0; ::curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); - Log( "HTTP response %1%", http_code ) ; + Trace( "HTTP response %1%", http_code ) ; if ( curl_code != CURLE_OK || http_code >= 400 ) { @@ -154,7 +154,7 @@ long Agent::Put( Receivable *dest, const http::Headers& hdr ) { - Log("HTTP PUT \"%1%\"", url ) ; + Trace("HTTP PUT \"%1%\"", url ) ; CURL *curl = m_pimpl->curl ; @@ -177,7 +177,7 @@ long Agent::Get( Receivable *dest, const http::Headers& hdr ) { - Log("HTTP GET \"%1%\"", url ) ; + Trace("HTTP GET \"%1%\"", url ) ; CURL *curl = m_pimpl->curl ; @@ -196,7 +196,7 @@ long Agent::Post( Receivable *dest, const http::Headers& hdr ) { - Log("HTTP POST \"%1%\" with \"%2%\"", url, data ) ; + Trace("HTTP POST \"%1%\" with \"%2%\"", url, data ) ; CURL *curl = m_pimpl->curl ; @@ -218,7 +218,7 @@ long Agent::Custom( Receivable *dest, const http::Headers& hdr ) { - Log("HTTP %2% \"%1%\"", url, method ) ; + Trace("HTTP %2% \"%1%\"", url, method ) ; CURL *curl = m_pimpl->curl ; diff --git a/libgrive/src/util/Destroy.hh b/libgrive/src/util/Destroy.hh new file mode 100644 index 0000000..acb11d2 --- /dev/null +++ b/libgrive/src/util/Destroy.hh @@ -0,0 +1,33 @@ +/* + 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 + +namespace gr { + +struct Destroy +{ + template + void operator()( T *t ) const + { + delete t ; + } +} ; + +} // end of namespace