From 6453de2cf67a3152e4e807a0721200ad6849a5d9 Mon Sep 17 00:00:00 2001 From: Matchman Green Date: Sun, 20 May 2012 14:57:25 +0800 Subject: [PATCH] start using state for synchronization --- libgrive/src/drive/Drive.cc | 2 +- libgrive/src/drive/Entry.cc | 72 +++++++++---------- libgrive/src/drive/Entry.hh | 13 ++-- libgrive/src/drive/Resource.cc | 109 ++++++++++++++++++----------- libgrive/src/drive/Resource.hh | 28 +++++--- libgrive/src/drive/ResourceTree.cc | 2 +- libgrive/src/drive/State.cc | 18 +++-- 7 files changed, 144 insertions(+), 100 deletions(-) diff --git a/libgrive/src/drive/Drive.cc b/libgrive/src/drive/Drive.cc index 87839a9..f97bb23 100644 --- a/libgrive/src/drive/Drive.cc +++ b/libgrive/src/drive/Drive.cc @@ -168,7 +168,7 @@ void Drive::Update() { http::Agent http ; std::for_each( m_state.begin(), m_state.end(), - boost::bind( &Resource::Update, _1, &http, m_http_hdr ) ) ; + boost::bind( &Resource::Sync, _1, &http, m_http_hdr ) ) ; } } // end of namespace diff --git a/libgrive/src/drive/Entry.cc b/libgrive/src/drive/Entry.cc index 2c39333..9bf9ff7 100644 --- a/libgrive/src/drive/Entry.cc +++ b/libgrive/src/drive/Entry.cc @@ -31,57 +31,52 @@ namespace gr { +/// construct an entry for the root folder +Entry::Entry( ) : + m_title ( "." ), + m_kind ( "folder" ), + m_resource_id ( "folder:root" ), + m_self_href ( root_href ) +{ +} + +/// construct an entry for remote Entry::Entry( const xml::Node& n ) { Update( n ) ; } -Entry::Entry( const std::string& title, const std::string& kind, const std::string& href ) : - m_title ( title ), - m_filename ( title ), - m_kind ( kind ), - m_self_href ( href ) -{ -} - /// construct an entry from a file or folder in local directory Entry::Entry( const fs::path& path ) : - m_title ( path.filename().string() ), - m_filename ( path.filename().string() ), - m_kind ( fs::is_directory(path) ? "folder" : "file" ), - m_server_md5 ( crypt::MD5( path ) ), - m_server_modified( os::FileMTime( path ) ) + m_title ( path.filename().string() ), + m_filename ( path.filename().string() ), + m_kind ( fs::is_directory(path) ? "folder" : "file" ), + m_md5 ( fs::is_directory(path) ? "" : crypt::MD5( path ) ), + m_mtime ( os::FileMTime( path ) ) { } void Entry::Update( const xml::Node& n ) { - m_title = n["title"] ; - m_etag = n["@gd:etag"] ; - m_filename = n["docs:suggestedFilename"] ; - m_content_src = n["content"]["@src"] ; - m_self_href = n["link"].Find( "@rel", "self" )["@href"] ; - m_server_modified = DateTime( n["updated"] ) ; + m_title = n["title"] ; + m_etag = n["@gd:etag"] ; + m_filename = n["docs:suggestedFilename"] ; + m_content_src = n["content"]["@src"] ; + m_self_href = n["link"].Find( "@rel", "self" )["@href"] ; + m_mtime = DateTime( n["updated"] ) ; - m_resource_id = n["gd:resourceId"] ; - m_server_md5 = n["docs:md5Checksum"] ; - m_kind = n["category"].Find( "@scheme", "http://schemas.google.com/g/2005#kind" )["@label"] ; - m_upload_link = n["link"].Find( "@rel", "http://schemas.google.com/g/2005#resumable-edit-media")["@href"] ; + m_resource_id = n["gd:resourceId"] ; + m_md5 = n["docs:md5Checksum"] ; + m_kind = n["category"].Find( "@scheme", "http://schemas.google.com/g/2005#kind" )["@label"] ; + m_upload_link = n["link"].Find( "@rel", "http://schemas.google.com/g/2005#resumable-edit-media")["@href"] ; m_parent_hrefs.clear( ) ; xml::NodeSet parents = n["link"].Find( "@rel", "http://schemas.google.com/docs/2007#parent" ) ; for ( xml::NodeSet::iterator i = parents.begin() ; i != parents.end() ; ++i ) m_parent_hrefs.push_back( (*i)["@href"] ) ; - if ( !m_parent_hrefs.empty() ) - m_parent_href = m_parent_hrefs.front() ; - // convert to lower case for easy comparison - std::transform( - m_server_md5.begin(), - m_server_md5.end(), - m_server_md5.begin(), - tolower ) ; + std::transform( m_md5.begin(), m_md5.end(), m_md5.begin(), tolower ) ; } const std::vector& Entry::ParentHrefs() const @@ -104,14 +99,14 @@ std::string Entry::Kind() const return m_kind ; } -std::string Entry::ServerMD5() const +std::string Entry::MD5() const { - return m_server_md5 ; + return m_md5 ; } -DateTime Entry::ServerModified() const +DateTime Entry::MTime() const { - return m_server_modified ; + return m_mtime ; } std::string Entry::SelfHref() const @@ -121,7 +116,7 @@ std::string Entry::SelfHref() const std::string Entry::ParentHref() const { - return m_parent_href ; + return m_parent_hrefs.empty() ? "" : m_parent_hrefs.front() ; } std::string Entry::ResourceID() const @@ -149,18 +144,17 @@ void Entry::Swap( Entry& e ) m_title.swap( e.m_title ) ; m_filename.swap( e.m_filename ) ; m_kind.swap( e.m_kind ) ; - m_server_md5.swap( e.m_server_md5 ) ; + m_md5.swap( e.m_md5 ) ; m_etag.swap( e.m_etag ) ; m_resource_id.swap( e.m_resource_id ) ; m_parent_hrefs.swap( e.m_parent_hrefs ) ; m_self_href.swap( e.m_self_href ) ; - m_parent_href.swap( e.m_parent_href ) ; m_content_src.swap( e.m_content_src ) ; m_upload_link.swap( e.m_upload_link ) ; - m_server_modified.Swap( e.m_server_modified ) ; + m_mtime.Swap( e.m_mtime ) ; } } // end of namespace diff --git a/libgrive/src/drive/Entry.hh b/libgrive/src/drive/Entry.hh index 4a0f982..dde3405 100644 --- a/libgrive/src/drive/Entry.hh +++ b/libgrive/src/drive/Entry.hh @@ -36,20 +36,21 @@ namespace xml /*! \brief corresponds to an "entry" in the resource feed This class is decodes an entry in the resource feed. It will stored the properties like - title, filename and ETag etc in member variables. + title, filename and ETag etc in member variables. Normally entries are created by + parsing the resource feed */ class Entry { public : + Entry( ) ; explicit Entry( const fs::path& path ) ; explicit Entry( const xml::Node& n ) ; - Entry( const std::string& title, const std::string& kind, const std::string& href ) ; std::string Title() const ; std::string Filename() const ; std::string Kind() const ; - std::string ServerMD5() const ; - DateTime ServerModified() const ; + std::string MD5() const ; + DateTime MTime() const ; std::string ResourceID() const ; std::string ETag() const ; @@ -69,7 +70,7 @@ private : std::string m_title ; std::string m_filename ; std::string m_kind ; - std::string m_server_md5 ; + std::string m_md5 ; std::string m_etag ; std::string m_resource_id ; @@ -80,7 +81,7 @@ private : std::string m_parent_href ; std::string m_upload_link ; - DateTime m_server_modified ; + DateTime m_mtime ; } ; } // end of namespace diff --git a/libgrive/src/drive/Resource.cc b/libgrive/src/drive/Resource.cc index 85ba866..fb9c56d 100644 --- a/libgrive/src/drive/Resource.cc +++ b/libgrive/src/drive/Resource.cc @@ -37,35 +37,61 @@ namespace gr { +/// default constructor creates the root folder +Resource::Resource() : + m_parent( 0 ), + m_state ( sync ) +{ +} + Resource::Resource( const xml::Node& entry ) : m_entry ( entry ), m_parent( 0 ), - m_state ( new_remote ) + m_state ( remote_new ) { } Resource::Resource( const Entry& entry, Resource *parent ) : m_entry ( entry ), m_parent( parent ), - m_state ( new_remote ) + m_state ( remote_new ) { } -Resource::Resource( - const std::string& name, - const std::string& kind, - const std::string& href ) : - m_entry ( name, kind, href ), +// Resource::Resource( +// const std::string& name, +// const std::string& kind, +// const std::string& href ) : +// m_entry ( name, kind, href ), +// m_parent( 0 ), +// m_state ( local_new ) +// { +// } + +Resource::Resource( const fs::path& path ) : + m_entry ( path ), m_parent( 0 ), - m_state ( new_local ) + m_state ( local_new ) { + } -void Resource::FromRemote( const Entry& e ) +/// Update the state according to information (i.e. Entry) from remote. This function +/// compares the modification time and checksum of both copies and determine which +/// one is newer. +void Resource::FromRemote( const Entry& remote ) { - Trace( "%1% state is %2%", e.Title(), m_state ) ; - m_state = sync ; - m_entry = e ; + // if checksum is equal, no need to compare the mtime + if ( remote.MD5() == m_entry.MD5() ) + m_state = sync ; + + // use mtime to check which one is more recent + else + { + m_state = ( remote.MTime() > m_entry.MTime() ? remote_changed : local_changed ) ; + } + + m_entry = remote ; } std::string Resource::SelfHref() const @@ -145,40 +171,39 @@ Resource* Resource::FindChild( const std::string& name ) return 0 ; } -void Resource::Update( http::Agent *http, const http::Headers& auth ) +// try to change the state to "sync" +void Resource::Sync( http::Agent *http, const http::Headers& auth ) { // no need to update for folders if ( IsFolder() ) return ; assert( m_parent != 0 ) ; - - bool changed = true ; - fs::path path = Path() ; - - Trace( "updating %1%", path ) ; - // compare checksum first if file exists - std::ifstream ifile( path.string().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 ) + switch ( m_state ) { - 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 ) ; - } + case local_new : + Trace( "file %1% doesn't exist in server. upload?", m_entry.Filename() ) ; + break ; + + case local_changed : + Trace( "file %1% changed in local", m_entry.Filename() ) ; + if ( Upload( http, auth ) ) + m_state = sync ; + break ; + + case remote_new : + case remote_changed : + Trace( "file %1% changed in remote", m_entry.Filename() ) ; + Download( http, Path(), auth ) ; + m_state = sync ; + break ; + + case sync : + Trace( "file %1% already in sync", m_entry.Filename() ) ; + + default : + break ; } } @@ -198,7 +223,13 @@ void Resource::Download( http::Agent* http, const fs::path& file, const http::He http::Download dl( file.string(), http::Download::NoChecksum() ) ; long r = http->Get( m_entry.ContentSrc(), &dl, auth ) ; if ( r <= 400 ) - os::SetFileTime( file, m_entry.ServerModified() ) ; + os::SetFileTime( file, m_entry.MTime() ) ; +} + +bool Resource::Upload( http::Agent* http, const http::Headers& auth ) +{ + std::ifstream ifile( Path().string().c_str(), std::ios::binary | std::ios::in ) ; + return Upload( http, ifile.rdbuf(), auth ) ; } bool Resource::Upload( http::Agent* http, std::streambuf *file, const http::Headers& auth ) diff --git a/libgrive/src/drive/Resource.hh b/libgrive/src/drive/Resource.hh index 92e1749..f184fb8 100644 --- a/libgrive/src/drive/Resource.hh +++ b/libgrive/src/drive/Resource.hh @@ -41,12 +41,14 @@ public : struct Error : virtual Exception {} ; public : + Resource() ; explicit Resource( const xml::Node& entry ) ; explicit Resource( const Entry& entry, Resource *parent = 0 ) ; - Resource( - const std::string& name, - const std::string& kind, - const std::string& href ) ; + explicit Resource( const fs::path& path ) ; +// Resource( +// const std::string& name, +// const std::string& kind, +// const std::string& href ) ; void Swap( Resource& coll ) ; // default copy ctor & op= are fine @@ -67,27 +69,35 @@ public : bool IsInRootTree() const ; void FromRemote( const Entry& e ) ; - void Update( http::Agent *http, const http::Headers& auth ) ; + void Sync( http::Agent *http, const http::Headers& auth ) ; void Delete( http::Agent* http, const http::Headers& auth ) ; private : /// State of the resource. indicating what to do with the resource enum State { - /// The best state: the file is the same in google drive and in local. + /// The best state: the file is the same in remote and in local. sync, - /// Resource created in local, but google drive does not have it. + /// Resource created in local, but remote does not have it. /// We should create the resource in google drive and upload new content - new_local, + local_new, + + /// Resource exists in both local & remote, but changes in local is newer + /// than remote. We should upload local copy to overwrite remote. + local_changed, /// Resource created in google drive, but not exist in local. /// We should download the file. - new_remote + remote_new, + + /// Resource exists in both local & remote, but remote is newer. + remote_changed } ; private : void Download( http::Agent* http, const fs::path& file, const http::Headers& auth ) const ; + bool Upload( http::Agent* http, const http::Headers& auth ) ; bool Upload( http::Agent* http, std::streambuf *file, const http::Headers& auth ) ; private : diff --git a/libgrive/src/drive/ResourceTree.cc b/libgrive/src/drive/ResourceTree.cc index e3f4923..f4e3d09 100644 --- a/libgrive/src/drive/ResourceTree.cc +++ b/libgrive/src/drive/ResourceTree.cc @@ -30,7 +30,7 @@ namespace gr { using namespace details ; ResourceTree::ResourceTree( ) : - m_root( new Resource( ".", "folder", root_href ) ) + m_root( new Resource ) { m_set.insert( m_root ) ; } diff --git a/libgrive/src/drive/State.cc b/libgrive/src/drive/State.cc index 71cfb39..2927305 100644 --- a/libgrive/src/drive/State.cc +++ b/libgrive/src/drive/State.cc @@ -75,7 +75,7 @@ void State::FromLocal( const fs::path& p, gr::Resource* folder ) { if ( fs::is_directory( i->path() ) ) { - Resource *c = new Resource( i->path().filename().string(), "folder", "" ) ; + Resource *c = new Resource( i->path() ) ; folder->AddChild( c ) ; m_folders.Insert( c ) ; @@ -85,7 +85,7 @@ void State::FromLocal( const fs::path& p, gr::Resource* folder ) Log( "file %1% is ignored by grive", i->path().filename().string(), log::info ) ; else { - Resource *c = new Resource( i->path().filename().string(), "file", "" ) ; + Resource *c = new Resource( i->path() ) ; folder->AddChild( c ) ; m_folders.Insert( c ) ; } @@ -150,12 +150,15 @@ bool State::Update( const Entry& e ) if ( parent != 0 ) { assert( parent->IsFolder() ) ; - + +Trace( "remote entry title %1%, filename %2%", e.Title(), e.Filename() ) ; // see if the entry already exist in local - Resource *child = parent->FindChild( e.Title() ) ; + std::string name = ( e.Kind() == "folder" ? e.Title() : e.Filename() ) ; + Resource *child = parent->FindChild( name ) ; if ( child != 0 ) { - assert( child == m_folders.FindByHref( e.SelfHref() ) ) ; +// Trace( "remote entry %1%, local %2%", e.Title(), child->Name() ) ; +// assert( child == m_folders.FindByHref( e.SelfHref() ) ) ; // since we are updating the ID and Href, we need to remove it and re-add it. m_folders.Update( child, e ) ; @@ -176,6 +179,11 @@ bool State::Update( const Entry& e ) fs::create_directories( child_path ) ; } } + else + { + Trace( "what here? %1%", e.Title() ) ; + } + return true ; } else