mirror of https://github.com/vitalif/grive2
not tracking the status of all files/dir in .grive_state
parent
167e2eaac5
commit
8b1b388713
|
@ -125,7 +125,7 @@ int main( int argc, char **argv )
|
|||
drive.Update() ;
|
||||
drive.SaveState() ;
|
||||
|
||||
// config.Save() ;
|
||||
config.Save() ;
|
||||
}
|
||||
catch ( gr::Exception& e )
|
||||
{
|
||||
|
|
|
@ -59,26 +59,13 @@ Drive::Drive( OAuth2& auth ) :
|
|||
m_http_hdr.push_back( "Authorization: Bearer " + m_auth.AccessToken() ) ;
|
||||
m_http_hdr.push_back( "GData-Version: 3.0" ) ;
|
||||
|
||||
http::Agent http ;
|
||||
http::XmlResponse xrsp ;
|
||||
http.Get( feed_metadata, &xrsp, m_http_hdr ) ;
|
||||
|
||||
std::string change_stamp = xrsp.Response()["docs:largestChangestamp"]["@value"] ;
|
||||
Trace( "change stamp is %1%", change_stamp ) ;
|
||||
|
||||
m_state.ChangeStamp( change_stamp ) ;
|
||||
m_state.FromLocal( "." ) ;
|
||||
|
||||
ConstructDirTree( &http ) ;
|
||||
http::Agent http ;
|
||||
SyncFolders( &http ) ;
|
||||
|
||||
std::string uri = feed_base + "?showfolders=true&showroot=true" ;
|
||||
/* if ( !change_stamp.empty() )
|
||||
{
|
||||
int ichangestamp = std::atoi( change_stamp.c_str() ) + 1 ;
|
||||
uri = (boost::format( "%1%&start-index=%2%" ) % uri % ichangestamp ).str() ;
|
||||
}
|
||||
*/
|
||||
http.Get( uri, &xrsp, m_http_hdr ) ;
|
||||
http::XmlResponse xrsp ;
|
||||
http.Get( feed_base + "?showfolders=true&showroot=true", &xrsp, m_http_hdr ) ;
|
||||
xml::Node resp = xrsp.Response() ;
|
||||
|
||||
m_resume_link = resp["link"].
|
||||
|
@ -133,7 +120,7 @@ void Drive::SaveState()
|
|||
m_state.Write( state_file ) ;
|
||||
}
|
||||
|
||||
void Drive::ConstructDirTree( http::Agent *http )
|
||||
void Drive::SyncFolders( http::Agent *http )
|
||||
{
|
||||
http::XmlResponse xml ;
|
||||
http->Get( feed_base + "/-/folder?max-results=50&showroot=true", &xml, m_http_hdr ) ;
|
||||
|
@ -175,8 +162,7 @@ void Drive::ConstructDirTree( http::Agent *http )
|
|||
void Drive::Update()
|
||||
{
|
||||
http::Agent http ;
|
||||
std::for_each( m_state.begin(), m_state.end(),
|
||||
boost::bind( &Resource::Sync, _1, &http, m_http_hdr ) ) ;
|
||||
m_state.Sync( &http, m_http_hdr ) ;
|
||||
}
|
||||
|
||||
} // end of namespace
|
||||
|
|
|
@ -47,7 +47,7 @@ public :
|
|||
struct Error : virtual Exception {} ;
|
||||
|
||||
private :
|
||||
void ConstructDirTree( http::Agent *http ) ;
|
||||
void SyncFolders( http::Agent *http ) ;
|
||||
void file();
|
||||
|
||||
private :
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include "CommonUri.hh"
|
||||
|
||||
#include "http/Download.hh"
|
||||
#include "http/ResponseLog.hh"
|
||||
// #include "http/ResponseLog.hh"
|
||||
#include "http/StringResponse.hh"
|
||||
#include "http/XmlResponse.hh"
|
||||
#include "protocol/Json.hh"
|
||||
|
@ -56,7 +56,7 @@ Resource::Resource() :
|
|||
m_state ( sync )
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
/// Construct from previously serialized JSON object. The state of the
|
||||
/// resource is treated as local_deleted by default. It is because the
|
||||
/// state will be updated by scanning the local directory. If the state
|
||||
|
@ -77,7 +77,7 @@ Resource::Resource( const Json& json, Resource *parent ) :
|
|||
// state as local_changed
|
||||
FromLocal() ;
|
||||
}
|
||||
|
||||
*/
|
||||
Resource::Resource( const xml::Node& entry ) :
|
||||
m_entry ( entry ),
|
||||
m_parent( 0 ),
|
||||
|
@ -114,7 +114,7 @@ void Resource::FromRemote( const Entry& remote )
|
|||
// if checksum is equal, no need to compare the mtime
|
||||
else if ( remote.MD5() == m_entry.MD5() )
|
||||
{
|
||||
Log( "MD5 matches: %1% is already in sync", Name(), log::verbose ) ;
|
||||
Log( "MD5 matches: %1% is already in sync", Path(), log::verbose ) ;
|
||||
m_state = sync ;
|
||||
}
|
||||
|
||||
|
@ -122,9 +122,14 @@ void Resource::FromRemote( const Entry& remote )
|
|||
else
|
||||
{
|
||||
assert( m_state == local_new || m_state == local_changed || m_state == local_deleted ) ;
|
||||
|
||||
m_state = ( remote.MTime() > m_entry.MTime() ? remote_changed : m_state ) ;
|
||||
m_state = ( m_state == local_new ? local_changed : m_state ) ;
|
||||
|
||||
// if remote is modified
|
||||
if ( remote.MTime() > m_entry.MTime() )
|
||||
m_state = remote_changed ;
|
||||
|
||||
// remote also has the file, so it's not new in local
|
||||
else if ( m_state == local_new || m_state == local_deleted )
|
||||
m_state = local_changed ;
|
||||
|
||||
Log( "%1% state is %2%", Name(), m_state, log::verbose ) ;
|
||||
}
|
||||
|
@ -132,37 +137,24 @@ void Resource::FromRemote( const Entry& remote )
|
|||
m_entry.AssignID( remote ) ;
|
||||
}
|
||||
|
||||
void Resource::FromLocal()
|
||||
/// Update the resource with the attributes of local file or directory. This
|
||||
/// function will propulate the fields in m_entry.
|
||||
void Resource::FromLocal( const DateTime& last_sync )
|
||||
{
|
||||
fs::path path = Path() ;
|
||||
assert( fs::exists( path ) ) ;
|
||||
|
||||
// root folder is always rsync
|
||||
if ( m_parent == 0 )
|
||||
m_state = sync ;
|
||||
|
||||
else if ( !fs::exists( path ) )
|
||||
{
|
||||
m_state = local_deleted ;
|
||||
Log( "%1% in state but not exist on disk: %2%", Name(), m_state ) ;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
m_state = local_new ;
|
||||
// assume file is local_deleted?? very strange. change it tomorrow
|
||||
DateTime mtime = os::FileMTime( path ) ;
|
||||
m_state = ( mtime > last_sync ? local_new : local_deleted ) ;
|
||||
|
||||
// no need to compare MD5 or mtime for directories
|
||||
if ( !IsFolder() )
|
||||
{
|
||||
// to save time, compare mtime before checksum
|
||||
DateTime mtime = os::FileMTime( path ) ;
|
||||
if ( mtime > m_entry.MTime() )
|
||||
{
|
||||
Log( "%1% mtime newer on disk: %2%", Name(), m_state ) ;
|
||||
m_entry.Update( crypt::MD5( path ), mtime ) ;
|
||||
}
|
||||
else
|
||||
Log( "%1% unchanged on disk: %2%", Name(), m_state ) ;
|
||||
}
|
||||
Log( "%1% found on disk: %2%", Name(), m_state ) ;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -260,31 +252,31 @@ void Resource::Sync( http::Agent *http, const http::Headers& auth )
|
|||
{
|
||||
case local_new :
|
||||
Log( "sync %1% doesn't exist in server. upload \"%2%\"?",
|
||||
Name(), m_parent->m_entry.CreateLink(), log::verbose ) ;
|
||||
Path(), m_parent->m_entry.CreateLink(), log::verbose ) ;
|
||||
|
||||
if ( Create( http, auth ) )
|
||||
m_state = sync ;
|
||||
break ;
|
||||
|
||||
case local_deleted :
|
||||
Log( "sync %1% deleted in local. delete?", m_entry.Filename(), log::verbose ) ;
|
||||
Log( "sync %1% deleted in local. delete?", Path(), log::verbose ) ;
|
||||
break ;
|
||||
|
||||
case local_changed :
|
||||
Log( "sync %1% changed in local", m_entry.Filename(), log::verbose ) ;
|
||||
Log( "sync %1% changed in local", Path(), log::verbose ) ;
|
||||
if ( EditContent( http, auth ) )
|
||||
m_state = sync ;
|
||||
break ;
|
||||
|
||||
case remote_new :
|
||||
case remote_changed :
|
||||
Log( "sync %1% changed in remote. download?", m_entry.Filename(), log::verbose ) ;
|
||||
Log( "sync %1% changed in remote. download?", Path(), log::verbose ) ;
|
||||
Download( http, Path(), auth ) ;
|
||||
m_state = sync ;
|
||||
break ;
|
||||
|
||||
case sync :
|
||||
Log( "sync %1% already in sync", m_entry.Filename(), log::verbose ) ;
|
||||
Log( "sync %1% already in sync", Path(), log::verbose ) ;
|
||||
break ;
|
||||
|
||||
default :
|
||||
|
|
|
@ -50,7 +50,7 @@ public :
|
|||
explicit Resource( const xml::Node& entry ) ;
|
||||
explicit Resource( const Entry& entry, Resource *parent = 0 ) ;
|
||||
explicit Resource( const fs::path& path ) ;
|
||||
explicit Resource( const Json& json, Resource *parent = 0 ) ;
|
||||
// explicit Resource( const Json& json, Resource *parent = 0 ) ;
|
||||
void Swap( Resource& coll ) ;
|
||||
|
||||
// default copy ctor & op= are fine
|
||||
|
@ -72,7 +72,7 @@ public :
|
|||
bool IsRoot() const ;
|
||||
|
||||
void FromRemote( const Entry& e ) ;
|
||||
void FromLocal() ;
|
||||
void FromLocal( const DateTime& last_sync ) ;
|
||||
|
||||
void Sync( http::Agent *http, const http::Headers& auth ) ;
|
||||
void Delete( http::Agent* http, const http::Headers& auth ) ;
|
||||
|
@ -109,7 +109,10 @@ private :
|
|||
remote_new,
|
||||
|
||||
/// Resource exists in both local & remote, but remote is newer.
|
||||
remote_changed
|
||||
remote_changed,
|
||||
|
||||
/// Resource delete in remote, need to delete in local
|
||||
remote_deleted
|
||||
} ;
|
||||
|
||||
friend std::ostream& operator<<( std::ostream& os, State s ) ;
|
||||
|
|
|
@ -177,7 +177,7 @@ ResourceTree::iterator ResourceTree::end()
|
|||
{
|
||||
return m_set.get<ByIdentity>().end() ;
|
||||
}
|
||||
|
||||
/*
|
||||
void ResourceTree::Read( const Json& json )
|
||||
{
|
||||
Clear() ;
|
||||
|
@ -205,5 +205,5 @@ Json ResourceTree::Serialize() const
|
|||
{
|
||||
return m_root->Serialize() ;
|
||||
}
|
||||
|
||||
*/
|
||||
} // end of namespace
|
||||
|
|
|
@ -89,12 +89,12 @@ public :
|
|||
iterator begin() ;
|
||||
iterator end() ;
|
||||
|
||||
void Read( const Json& json ) ;
|
||||
Json Serialize() const ;
|
||||
// void Read( const Json& json ) ;
|
||||
// Json Serialize() const ;
|
||||
|
||||
private :
|
||||
void Clear() ;
|
||||
void AddTree( Resource *node, const Json& json ) ;
|
||||
// void AddTree( Resource *node, const Json& json ) ;
|
||||
|
||||
private :
|
||||
details::Folders m_set ;
|
||||
|
|
|
@ -43,16 +43,6 @@ State::State( const fs::path& filename )
|
|||
Read( filename ) ;
|
||||
}
|
||||
|
||||
std::string State::ChangeStamp() const
|
||||
{
|
||||
return m_change_stamp ;
|
||||
}
|
||||
|
||||
void State::ChangeStamp( const std::string& cs )
|
||||
{
|
||||
m_change_stamp = cs ;
|
||||
}
|
||||
|
||||
/// Synchronize local directory. Build up the resource tree from files and folders
|
||||
/// of local directory.
|
||||
void State::FromLocal( const fs::path& p )
|
||||
|
@ -66,7 +56,7 @@ void State::FromLocal( const fs::path& p, gr::Resource* folder )
|
|||
assert( folder->IsFolder() ) ;
|
||||
|
||||
// sync the folder itself
|
||||
folder->FromLocal() ;
|
||||
folder->FromLocal( m_last_sync ) ;
|
||||
|
||||
for ( fs::directory_iterator i( p ) ; i != fs::directory_iterator() ; ++i )
|
||||
{
|
||||
|
@ -82,12 +72,13 @@ void State::FromLocal( const fs::path& p, gr::Resource* folder )
|
|||
Resource *c = folder->FindChild( fname ) ;
|
||||
if ( c == 0 )
|
||||
{
|
||||
Log( "detected new file %1% in local", fname, log::verbose ) ;
|
||||
c = new Resource( i->path() ) ;
|
||||
folder->AddChild( c ) ;
|
||||
m_res.Insert( c ) ;
|
||||
}
|
||||
|
||||
c->FromLocal( m_last_sync ) ;
|
||||
|
||||
if ( fs::is_directory( i->path() ) )
|
||||
FromLocal( *i, c ) ;
|
||||
}
|
||||
|
@ -157,15 +148,23 @@ bool State::Update( const Entry& e )
|
|||
// the directory
|
||||
else if ( e.Kind() == "folder" || !e.Filename().empty() )
|
||||
{
|
||||
child = new Resource( e ) ;
|
||||
parent->AddChild( child ) ;
|
||||
m_res.Insert( child ) ;
|
||||
|
||||
fs::path child_path = child->Path() ;
|
||||
if ( child->IsFolder() && !fs::is_directory( child_path ) )
|
||||
// TODO: compare the last sync time to determine which one is newer
|
||||
if ( e.MTime() > m_last_sync )
|
||||
{
|
||||
Log( "creating %1% directory", child_path, log::info ) ;
|
||||
fs::create_directories( child_path ) ;
|
||||
child = new Resource( e ) ;
|
||||
parent->AddChild( child ) ;
|
||||
m_res.Insert( child ) ;
|
||||
|
||||
fs::path child_path = child->Path() ;
|
||||
if ( child->IsFolder() && !fs::is_directory( child_path ) )
|
||||
{
|
||||
Log( "creating %1% directory", child_path, log::info ) ;
|
||||
fs::create_directories( child_path ) ;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Trace( "should I delete the local %1%/%2%", parent->Path(), e.Filename() ) ;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -202,23 +201,40 @@ State::iterator State::end()
|
|||
|
||||
void State::Read( const fs::path& filename )
|
||||
{
|
||||
if ( fs::exists( filename ) )
|
||||
try
|
||||
{
|
||||
Json json = Json::ParseFile( filename.string() ) ;
|
||||
|
||||
m_change_stamp = json["change_stamp"].Str() ;
|
||||
m_res.Read( json["rtree"] ) ;
|
||||
Json last_sync = json["last_sync"] ;
|
||||
m_last_sync.Assign(
|
||||
last_sync["sec"].Int(),
|
||||
last_sync["nsec"].Int() ) ;
|
||||
}
|
||||
catch ( Exception& )
|
||||
{
|
||||
m_last_sync.Assign(0) ;
|
||||
}
|
||||
}
|
||||
|
||||
void State::Write( const fs::path& filename ) const
|
||||
{
|
||||
Json last_sync ;
|
||||
last_sync.Add( "sec", Json(m_last_sync.Sec() ) );
|
||||
last_sync.Add( "nsec", Json(m_last_sync.NanoSec() ) );
|
||||
|
||||
Json result ;
|
||||
result.Add( "change_stamp", Json( m_change_stamp ) ) ;
|
||||
result.Add( "rtree", m_res.Serialize() ) ;
|
||||
result.Add( "last_sync", last_sync ) ;
|
||||
|
||||
std::ofstream fs( filename.string().c_str() ) ;
|
||||
fs << result ;
|
||||
}
|
||||
|
||||
void State::Sync( http::Agent *http, const http::Headers& auth )
|
||||
{
|
||||
std::for_each( m_res.begin(), m_res.end(),
|
||||
boost::bind( &Resource::Sync, _1, http, auth ) ) ;
|
||||
|
||||
m_last_sync = DateTime::Now() ;
|
||||
}
|
||||
|
||||
} // end of namespace
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "ResourceTree.hh"
|
||||
|
||||
#include "http/Agent.hh"
|
||||
#include "util/DateTime.hh"
|
||||
#include "util/FileSystem.hh"
|
||||
|
||||
#include <memory>
|
||||
|
@ -45,13 +48,12 @@ public :
|
|||
void Read( const fs::path& filename ) ;
|
||||
void Write( const fs::path& filename ) const ;
|
||||
|
||||
std::string ChangeStamp() const ;
|
||||
void ChangeStamp( const std::string& cs ) ;
|
||||
|
||||
Resource* FindByHref( const std::string& href ) ;
|
||||
Resource* FindByID( const std::string& id ) ;
|
||||
Resource* Find( const fs::path& path ) ;
|
||||
|
||||
void Sync( http::Agent *http, const http::Headers& auth ) ;
|
||||
|
||||
iterator begin() ;
|
||||
iterator end() ;
|
||||
|
||||
|
@ -61,8 +63,8 @@ private :
|
|||
std::size_t TryResolveEntry() ;
|
||||
|
||||
private :
|
||||
ResourceTree m_res ; ;
|
||||
std::string m_change_stamp ;
|
||||
ResourceTree m_res ;
|
||||
DateTime m_last_sync ;
|
||||
|
||||
std::vector<Entry> m_unresolved ;
|
||||
} ;
|
||||
|
|
|
@ -19,6 +19,15 @@
|
|||
|
||||
#include "DateTime.hh"
|
||||
|
||||
#include "Exception.hh"
|
||||
|
||||
// boost headers
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <boost/exception/errinfo_api_function.hpp>
|
||||
#include <boost/exception/errinfo_at_line.hpp>
|
||||
#include <boost/exception/errinfo_errno.hpp>
|
||||
#include <boost/exception/info.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <cassert>
|
||||
|
@ -55,15 +64,30 @@ DateTime::DateTime( const std::string& iso ) :
|
|||
}
|
||||
}
|
||||
|
||||
DateTime::DateTime( std::time_t sec, unsigned long nsec ) :
|
||||
m_sec ( sec + nsec / 1000000000 ),
|
||||
m_nsec ( nsec % 1000000000 )
|
||||
DateTime::DateTime( std::time_t sec, unsigned long nsec )
|
||||
{
|
||||
Assign( sec, nsec ) ;
|
||||
}
|
||||
|
||||
void DateTime::Assign( std::time_t sec, unsigned long nsec )
|
||||
{
|
||||
m_sec = sec + nsec / 1000000000 ;
|
||||
m_nsec = nsec % 1000000000 ;
|
||||
}
|
||||
|
||||
DateTime DateTime::Now()
|
||||
{
|
||||
return DateTime( std::time(0) ) ;
|
||||
struct timeval tv = {} ;
|
||||
if ( ::gettimeofday( &tv, 0 ) != 0 )
|
||||
{
|
||||
BOOST_THROW_EXCEPTION(
|
||||
Exception()
|
||||
<< boost::errinfo_api_function("gettimeofday")
|
||||
<< boost::errinfo_errno(errno)
|
||||
) ;
|
||||
}
|
||||
|
||||
return DateTime( tv.tv_sec, tv.tv_usec * 1000 ) ;
|
||||
}
|
||||
|
||||
std::string DateTime::Format( const std::string& format ) const
|
||||
|
|
|
@ -34,6 +34,8 @@ public :
|
|||
explicit DateTime( const std::string& iso ) ;
|
||||
explicit DateTime( std::time_t sec, unsigned long nsec = 0 ) ;
|
||||
|
||||
void Assign( std::time_t sec, unsigned long nsec = 0 ) ;
|
||||
|
||||
static DateTime Now() ;
|
||||
|
||||
std::time_t Sec( ) const ;
|
||||
|
|
|
@ -39,7 +39,7 @@ void ResourceTest::TestNormal( )
|
|||
Json json ;
|
||||
json.Add( "name", Json( "abc.txt" ) ) ;
|
||||
|
||||
Resource subject( json, 0 ) ;
|
||||
// Resource subject( json, 0 ) ;
|
||||
}
|
||||
|
||||
} // end of namespace grut
|
||||
|
|
Loading…
Reference in New Issue