minor refactoring

pull/40/head
Matchman Green 2012-05-19 17:14:04 +08:00
parent aa03b212f7
commit b46c9debf8
12 changed files with 92 additions and 319 deletions

View File

@ -21,7 +21,6 @@
#include "CommonUri.hh"
#include "Entry.hh"
#include "File.hh"
#include "http/Agent.hh"
#include "http/ResponseLog.hh"
@ -99,7 +98,8 @@ Drive::Drive( OAuth2& auth ) :
{
Resource *p = m_state.FindFolderByHref( file.ParentHref() ) ;
if ( p != 0 && p->IsInRootTree() )
UpdateFile( file, *p, &http ) ;
// m_state.OnEntry( file ) ;
UpdateFile( file, p, &http ) ;
else
Log( "file \"%1%\" parent doesn't exist, ignored", file.Title() ) ;
}
@ -163,14 +163,16 @@ void Drive::ConstructDirTree( http::Agent *http )
m_state.ResolveEntry() ;
}
void Drive::UpdateFile( Entry& entry, Resource& parent, http::Agent *http )
void Drive::UpdateFile( Entry& entry, Resource *parent, http::Agent *http )
{
assert( parent != 0 ) ;
// only handle uploaded files
if ( !entry.Filename().empty() )
{
Resource *file = new Resource( entry, &parent ) ;
Resource *file = new Resource( entry ) ;
parent->AddChild( file ) ;
m_files.push_back( file ) ;
parent.AddLeaf( file ) ;
Trace( "%1% ID = %2%", file->Path(), file->ResourceID() ) ;
}
@ -182,8 +184,6 @@ void Drive::UpdateFile( Entry& entry, Resource& parent, http::Agent *http )
void Drive::Update()
{
Trace( "updating %1% files", m_files.size() ) ;
http::Agent http ;
std::for_each( m_files.begin(), m_files.end(),
boost::bind( &Resource::Update, _1, &http, m_http_hdr ) ) ;

View File

@ -58,7 +58,7 @@ public :
struct Error : virtual Exception {} ;
private :
void UpdateFile( Entry& file, Resource& parent, http::Agent *http ) ;
void UpdateFile( Entry& file, Resource *parent, http::Agent *http ) ;
void ConstructDirTree( http::Agent *http ) ;

View File

@ -18,6 +18,7 @@
*/
#include "Entry.hh"
#include "CommonUri.hh"
#include "util/Log.hh"
#include "util/OS.hh"
@ -38,9 +39,11 @@ Entry::Entry( const xml::Node& n )
Update( n ) ;
}
Entry::Entry( const std::string& title, const std::string& href ) :
m_title( title ),
m_self_href( href )
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 )
{
}

View File

@ -44,7 +44,7 @@ class Entry
public :
explicit Entry( const Path& file ) ;
explicit Entry( const xml::Node& n ) ;
Entry( const std::string& title, const std::string& href ) ;
Entry( const std::string& title, const std::string& kind, const std::string& href ) ;
std::string Title() const ;
std::string Filename() const ;

View File

@ -1,160 +0,0 @@
/*
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 "Resource.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/OS.hh"
#include "util/Log.hh"
#include "xml/Node.hh"
#include <cassert>
#include <fstream>
namespace gr {
File::File( const Entry& entry, const Resource *parent ) :
m_entry ( entry ),
m_parent ( parent )
{
}
void File::Update( http::Agent *http, const http::Headers& auth )
{
assert( m_parent != 0 ) ;
bool changed = true ;
fs::path path = 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 )
{
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 fs::path& file, const http::Headers& auth ) const
{
Log( "Downloading %1%", file ) ;
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() ) ;
}
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 =
"<?xml version='1.0' encoding='UTF-8'?>\n"
"<entry xmlns=\"http://www.w3.org/2005/Atom\" xmlns:docs=\"http://schemas.google.com/docs/2007\">"
"<category scheme=\"http://schemas.google.com/g/2005#kind\" "
"term=\"http://schemas.google.com/docs/2007#file\"/>"
"<title>" + m_entry.Filename() + "</title>"
"</entry>" ;
std::string data(
(std::istreambuf_iterator<char>(file)),
(std::istreambuf_iterator<char>()) ) ;
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() ) ;
m_entry.Update( xml.Response() ) ;
return true ;
}
fs::path File::Path() const
{
assert( m_parent != 0 ) ;
return m_parent->Path() / m_entry.Filename() ;
}
std::string File::ResourceID() const
{
return m_entry.ResourceID() ;
}
} // end of namespace

View File

@ -1,57 +0,0 @@
/*
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 "util/FileSystem.hh"
#include <iosfwd>
namespace gr {
namespace http
{
class Agent ;
}
class Resource ;
class File
{
public :
explicit File( const Entry& e, const Resource *parent ) ;
void Update( http::Agent *http, const http::Headers& auth ) ;
void Delete( http::Agent* http, const http::Headers& auth ) ;
fs::path Path() const ;
std::string ResourceID() const ;
private :
void Download( http::Agent* http, const fs::path& file, const http::Headers& auth ) const ;
bool Upload( http::Agent* http, std::streambuf *file, const http::Headers& auth ) ;
private :
Entry m_entry ;
const Resource *m_parent ;
} ;
} // end of namespace

View File

@ -30,7 +30,7 @@ namespace gr {
using namespace details ;
FolderSet::FolderSet( ) :
m_root( new Resource( ".", root_href ) )
m_root( new Resource( ".", "folder", root_href ) )
{
m_set.insert( m_root ) ;
}
@ -128,4 +128,14 @@ void FolderSet::Update( Resource *coll, const Entry& e )
ReInsert( coll ) ;
}
FolderSet::iterator FolderSet::begin()
{
return m_set.get<ByIdentity>().begin() ;
}
FolderSet::iterator FolderSet::end()
{
return m_set.get<ByIdentity>().end() ;
}
} // end of namespace

View File

@ -58,6 +58,9 @@ namespace details
*/
class FolderSet
{
public :
typedef details::Set::iterator iterator ;
public :
FolderSet( ) ;
FolderSet( const FolderSet& fs ) ;
@ -79,7 +82,10 @@ public :
Resource* Root() ;
const Resource* Root() const ;
iterator begin() ;
iterator end() ;
private :
details::Folders m_set ;
Resource* m_root ;

View File

@ -50,9 +50,10 @@ Resource::Resource( const Entry& entry, Resource *parent ) :
}
Resource::Resource(
const std::string& title,
const std::string& name,
const std::string& kind,
const std::string& href ) :
m_entry ( title, href ),
m_entry ( name, kind, href ),
m_parent( 0 )
{
}
@ -67,9 +68,9 @@ std::string Resource::SelfHref() const
return m_entry.SelfHref() ;
}
std::string Resource::Title() const
std::string Resource::Name() const
{
return m_entry.Title() ;
return IsFolder() ? m_entry.Title() : m_entry.Filename() ;
}
std::string Resource::ResourceID() const
@ -102,35 +103,11 @@ void Resource::AddChild( Resource *child )
m_child.push_back( child ) ;
}
void Resource::AddLeaf( Resource *file )
{
assert( file != 0 ) ;
assert( !file->IsFolder() ) ;
m_leaf.push_back( file ) ;
}
void Resource::Swap( Resource& coll )
{
m_entry.Swap( coll.m_entry ) ;
std::swap( m_parent, coll.m_parent ) ;
m_child.swap( coll.m_child ) ;
m_leaf.swap( coll.m_leaf ) ;
}
void Resource::CreateSubDir( const fs::path& prefix )
{
fs::path dir = prefix / m_entry.Title() ;
// Trace( "dir = %1%, path = %2%", dir, Path() ) ;
// assert( dir == Path() ) ;
fs::create_directories( dir ) ;
for ( std::vector<Resource*>::iterator i = m_child.begin() ; i != m_child.end() ; ++i )
{
assert( (*i)->m_parent == this ) ;
if ( (*i)->IsFolder() )
(*i)->CreateSubDir( dir ) ;
}
}
bool Resource::IsFolder() const
@ -141,9 +118,8 @@ bool Resource::IsFolder() const
fs::path Resource::Path() const
{
assert( m_parent != this ) ;
std::string name = (IsFolder() ? m_entry.Title() : m_entry.Filename() ) ;
return m_parent != 0 ? (m_parent->Path() / name) : "." ;
return m_parent != 0 ? (m_parent->Path() / Name()) : "." ;
}
bool Resource::IsInRootTree() const
@ -151,18 +127,17 @@ bool Resource::IsInRootTree() const
return m_parent == 0 ? (SelfHref() == root_href) : m_parent->IsInRootTree() ;
}
Resource* Resource::FindChild( const std::string& title )
Resource* Resource::FindChild( const std::string& name )
{
for ( std::vector<Resource*>::iterator i = m_child.begin() ; i != m_child.end() ; ++i )
{
assert( (*i)->m_parent == this ) ;
if ( (*i)->Title() == title )
if ( (*i)->Name() == name )
return *i ;
}
return 0 ;
}
void Resource::Update( http::Agent *http, const http::Headers& auth )
{
assert( m_parent != 0 ) ;

View File

@ -32,15 +32,21 @@ namespace gr {
class Resource
{
public :
struct Error : virtual Exception {} ;
public :
explicit Resource( const xml::Node& entry ) ;
explicit Resource( const Entry& entry, Resource *parent = 0 ) ;
Resource( const std::string& title, const std::string& href ) ;
Resource( const std::string& title, Resource *parent ) ;
Resource(
const std::string& name,
const std::string& kind,
const std::string& href ) ;
void Swap( Resource& coll ) ;
// default copy ctor & op= are fine
std::string Title() const ;
std::string Name() const ;
std::string SelfHref() const ;
std::string ResourceID() const ;
@ -54,14 +60,6 @@ public :
bool IsFolder() const ;
void AddChild( Resource *child ) ;
void AddLeaf( Resource *file ) ;
void Swap( Resource& coll ) ;
// traversing the tree
void CreateSubDir( const fs::path& prefix ) ;
struct Error : virtual Exception {} ;
Resource* FindChild( const std::string& title ) ;
void Update( const Entry& e ) ;
@ -74,12 +72,11 @@ private :
bool Upload( http::Agent* http, std::streambuf *file, const http::Headers& auth ) ;
private :
Entry m_entry ;
Entry m_entry ;
// not owned
Resource *m_parent ;
std::vector<Resource*> m_child ;
std::vector<Resource*> m_leaf ;
} ;
} // end of namespace

View File

@ -38,17 +38,7 @@
namespace gr {
struct State::Impl
{
// ResourceSet rs ;
FolderSet folders ;
std::string change_stamp ;
std::vector<Entry> unresolved ;
} ;
State::State( const fs::path& filename ) :
m_impl( new Impl )
State::State( const fs::path& filename )
{
if ( fs::exists( filename ) )
Read( filename );
@ -56,22 +46,22 @@ State::State( const fs::path& filename ) :
void State::Read( const fs::path& filename )
{
Trace( "reading %1%", filename ) ;
Trace( "reading %1%", filename ) ;
}
std::string State::ChangeStamp() const
{
return m_impl->change_stamp ;
return m_change_stamp ;
}
void State::ChangeStamp( const std::string& cs )
{
m_impl->change_stamp = cs ;
m_change_stamp = cs ;
}
void State::Sync( const fs::path& p )
{
Sync( p, m_impl->folders.Root() ) ;
Sync( p, m_folders.Root() ) ;
}
void State::Sync( const boost::filesystem3::path& p, gr::Resource* folder )
@ -82,9 +72,9 @@ void State::Sync( const boost::filesystem3::path& p, gr::Resource* folder )
{
if ( fs::is_directory( i->path() ) )
{
Resource *c = new Resource( i->path().filename().string(), "" ) ;
Resource *c = new Resource( i->path().filename().string(), "folder", "" ) ;
folder->AddChild( c ) ;
m_impl->folders.Insert( c ) ;
m_folders.Insert( c ) ;
Sync( *i, c ) ;
}
@ -96,7 +86,7 @@ void State::Sync( const boost::filesystem3::path& p, gr::Resource* folder )
void State::Write( const fs::path& filename ) const
{
Json result ;
result.Add( "change_stamp", Json( m_impl->change_stamp ) ) ;
result.Add( "change_stamp", Json( m_change_stamp ) ) ;
std::ofstream fs( filename.string().c_str() ) ;
fs << result ;
@ -110,36 +100,28 @@ void State::OnEntry( const Entry& e )
{
if ( !Update( e ) )
{
Trace( "can't resolve folder %1%", e.Title() ) ;
m_impl->unresolved.push_back( e ) ;
m_unresolved.push_back( e ) ;
}
}
void State::ResolveEntry()
{
Trace( "trying to resolve %1% entries", m_impl->unresolved.size() ) ;
while ( !m_impl->unresolved.empty() )
while ( !m_unresolved.empty() )
{
if ( TryResolveEntry() == 0 )
{
Trace( "cannot make progress" ) ;
break ;
}
}
Trace( "entries left = %1%", m_impl->unresolved.size() ) ;
}
std::size_t State::TryResolveEntry()
{
assert( !m_impl->unresolved.empty() ) ;
assert( !m_unresolved.empty() ) ;
std::size_t count = 0 ;
std::vector<Entry>& en = m_impl->unresolved ;
std::vector<Entry>& en = m_unresolved ;
for ( std::vector<Entry>::iterator i = en.begin() ; i != en.end() ; )
{
Trace( "resolving %1%", i->Title() ) ;
if ( Update( *i ) )
{
i = en.erase( i ) ;
@ -153,29 +135,30 @@ std::size_t State::TryResolveEntry()
bool State::Update( const Entry& e )
{
Resource *parent = m_impl->folders.FindByHref( e.ParentHref() ) ;
Resource *parent = m_folders.FindByHref( e.ParentHref() ) ;
if ( parent != 0 )
{
Trace( "found parent of folder %1%: %2%", e.Title(), parent->Title() ) ;
// see if the entry already exist in local
Resource *child = parent->FindChild( e.Title() ) ;
if ( child != 0 )
{
// since we are updating the ID and Href, we need to remove it and re-add it.
m_impl->folders.Update( child, e ) ;
m_folders.Update( child, e ) ;
}
// folder entry exist in google drive, but not local. we should create
// the directory
else
else if ( e.Kind() == "folder" || !e.Filename().empty() )
{
child = new Resource( e ) ;
parent->AddChild( child ) ;
m_impl->folders.Insert( child ) ;
m_folders.Insert( child ) ;
Trace( "creating %1% directory", child->Path() ) ;
fs::create_directories( child->Path() ) ;
if ( child->IsFolder() )
{
Log( "creating %1% directory", child->Path(), log::info ) ;
fs::create_directories( child->Path() ) ;
}
}
return true ;
}
@ -185,7 +168,17 @@ bool State::Update( const Entry& e )
Resource* State::FindFolderByHref( const std::string& href )
{
return m_impl->folders.FindByHref( href ) ;
return m_folders.FindByHref( href ) ;
}
State::iterator State::begin()
{
return m_folders.begin() ;
}
State::iterator State::end()
{
return m_folders.end() ;
}
} // end of namespace

View File

@ -32,6 +32,9 @@ class Entry ;
class State
{
public :
typedef FolderSet::iterator iterator ;
public :
explicit State( const fs::path& filename ) ;
@ -51,16 +54,19 @@ public :
Resource* FindFolderByHref( const std::string& href ) ;
Resource* FindFolderByID( const std::string& id ) ;
iterator begin() ;
iterator end() ;
private :
void Sync( const fs::path& p, Resource *folder ) ;
bool Update( const Entry& e ) ;
std::size_t TryResolveEntry() ;
private :
struct Impl ;
std::auto_ptr<Impl> m_impl ;
FolderSet m_folders ;
std::string m_change_stamp ;
FolderSet m_folders ;
std::vector<Entry> m_unresolved ;
} ;
} // end of namespace