From 788219829379d852a16bd1c8da2730d43a982b12 Mon Sep 17 00:00:00 2001 From: Matchman Green Date: Sat, 19 May 2012 16:18:33 +0800 Subject: [PATCH] merged the File class to Resource --- libgrive/src/drive/Drive.cc | 9 ++- libgrive/src/drive/Drive.hh | 2 +- libgrive/src/drive/File.cc | 4 +- libgrive/src/drive/FolderSet.cc | 8 +- libgrive/src/drive/Resource.cc | 137 ++++++++++++++++++++++++++++++-- libgrive/src/drive/Resource.hh | 25 ++++-- libgrive/src/drive/State.cc | 99 +---------------------- 7 files changed, 168 insertions(+), 116 deletions(-) diff --git a/libgrive/src/drive/Drive.cc b/libgrive/src/drive/Drive.cc index fc27b15..979110b 100644 --- a/libgrive/src/drive/Drive.cc +++ b/libgrive/src/drive/Drive.cc @@ -30,7 +30,6 @@ #include "protocol/OAuth2.hh" #include "util/Destroy.hh" #include "util/Log.hh" -//#include "util/Path.hh" #include "xml/Node.hh" #include "xml/NodeSet.hh" @@ -229,11 +228,11 @@ void Drive::UpdateFile( Entry& entry, Resource& parent, http::Agent *http ) // only handle uploaded files if ( !entry.Filename().empty() ) { - File *file = new File( entry, &parent ) ; + Resource *file = new Resource( entry, &parent ) ; m_files.push_back( file ) ; parent.AddLeaf( file ) ; -// Trace( "%1% ID = %2%", file->Path(), file->ResourceID() ) ; + Trace( "%1% ID = %2%", file->Path(), file->ResourceID() ) ; // m_state.SetId( file->Path(), file->ResourceID() ) ; @@ -247,9 +246,11 @@ 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( &File::Update, _1, &http, m_http_hdr ) ) ; + boost::bind( &Resource::Update, _1, &http, m_http_hdr ) ) ; } } // end of namespace diff --git a/libgrive/src/drive/Drive.hh b/libgrive/src/drive/Drive.hh index bb87ddb..ec009c1 100644 --- a/libgrive/src/drive/Drive.hh +++ b/libgrive/src/drive/Drive.hh @@ -72,7 +72,7 @@ private : std::string m_resume_link ; FolderList m_coll ; - std::vector m_files ; + std::vector m_files ; State m_state ; } ; diff --git a/libgrive/src/drive/File.cc b/libgrive/src/drive/File.cc index fd55d8e..64daaa6 100644 --- a/libgrive/src/drive/File.cc +++ b/libgrive/src/drive/File.cc @@ -48,7 +48,7 @@ void File::Update( http::Agent *http, const http::Headers& auth ) assert( m_parent != 0 ) ; bool changed = true ; - fs::path path = m_parent->Dir() / m_entry.Filename() ; + fs::path path = Path() ; // compare checksum first if file exists std::ifstream ifile( path.string().c_str(), std::ios::binary | std::ios::in ) ; @@ -149,7 +149,7 @@ bool File::Upload( http::Agent* http, std::streambuf *file, const http::Headers& fs::path File::Path() const { assert( m_parent != 0 ) ; - return m_parent->Dir() / m_entry.Filename() ; + return m_parent->Path() / m_entry.Filename() ; } std::string File::ResourceID() const diff --git a/libgrive/src/drive/FolderSet.cc b/libgrive/src/drive/FolderSet.cc index 803b01b..ecb5426 100644 --- a/libgrive/src/drive/FolderSet.cc +++ b/libgrive/src/drive/FolderSet.cc @@ -23,6 +23,7 @@ #include "util/Destroy.hh" #include +#include namespace gr { @@ -34,7 +35,8 @@ FolderSet::FolderSet( ) : m_set.insert( m_root ) ; } -FolderSet::FolderSet( const FolderSet& fs ) +FolderSet::FolderSet( const FolderSet& fs ) : + m_root( 0 ) { const Set& s = fs.m_set.get() ; for ( Set::const_iterator i = s.begin() ; i != s.end() ; ++i ) @@ -45,6 +47,8 @@ FolderSet::FolderSet( const FolderSet& fs ) m_set.insert( c ) ; } + + assert( m_root != 0 ) ; } FolderSet::~FolderSet( ) @@ -56,11 +60,13 @@ FolderSet::~FolderSet( ) Resource* FolderSet::Root() { + assert( m_root != 0 ) ; return m_root ; } const Resource* FolderSet::Root() const { + assert( m_root != 0 ) ; return m_root ; } diff --git a/libgrive/src/drive/Resource.cc b/libgrive/src/drive/Resource.cc index cae03e5..b87f4ee 100644 --- a/libgrive/src/drive/Resource.cc +++ b/libgrive/src/drive/Resource.cc @@ -20,11 +20,17 @@ #include "Resource.hh" #include "CommonUri.hh" +#include "http/Download.hh" +#include "http/StringResponse.hh" +#include "http/XmlResponse.hh" +#include "util/Crypt.hh" +#include "util/Log.hh" #include "util/OS.hh" #include "xml/Node.hh" #include "xml/NodeSet.hh" #include +#include // for debugging #include @@ -37,9 +43,9 @@ Resource::Resource( const xml::Node& entry ) : { } -Resource::Resource( const Entry& entry ) : +Resource::Resource( const Entry& entry, Resource *parent ) : m_entry ( entry ), - m_parent ( 0 ) + m_parent ( parent ) { } @@ -96,8 +102,10 @@ void Resource::AddChild( Resource *child ) m_child.push_back( child ) ; } -void Resource::AddLeaf( File *file ) +void Resource::AddLeaf( Resource *file ) { + assert( file != 0 ) ; + assert( !file->IsFolder() ) ; m_leaf.push_back( file ) ; } @@ -112,19 +120,30 @@ void Resource::Swap( Resource& coll ) 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::iterator i = m_child.begin() ; i != m_child.end() ; ++i ) { assert( (*i)->m_parent == this ) ; - (*i)->CreateSubDir( dir ) ; + if ( (*i)->IsFolder() ) + (*i)->CreateSubDir( dir ) ; } } -fs::path Resource::Dir() const +bool Resource::IsFolder() const +{ + return m_entry.Kind() == "folder" ; +} + +fs::path Resource::Path() const { assert( m_parent != this ) ; - return m_parent != 0 ? (m_parent->Dir() / m_entry.Title()) : "." ; + std::string name = (IsFolder() ? m_entry.Title() : m_entry.Filename() ) ; + + return m_parent != 0 ? (m_parent->Path() / name) : "." ; } bool Resource::IsInRootTree() const @@ -143,6 +162,112 @@ Resource* Resource::FindChild( const std::string& title ) return 0 ; } + +void Resource::Update( http::Agent *http, const http::Headers& auth ) +{ + 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 ) + { + 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 Resource::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 Resource::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 Resource::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() ) ; + + m_entry.Update( xml.Response() ) ; + + return true ; +} + } // end of namespace namespace std diff --git a/libgrive/src/drive/Resource.hh b/libgrive/src/drive/Resource.hh index 211dbed..d0c620a 100644 --- a/libgrive/src/drive/Resource.hh +++ b/libgrive/src/drive/Resource.hh @@ -20,21 +20,21 @@ #pragma once #include "Entry.hh" +#include "http/Agent.hh" #include "util/Exception.hh" #include "util/FileSystem.hh" #include #include +#include namespace gr { -class File ; - class Resource { public : explicit Resource( const xml::Node& entry ) ; - explicit Resource( const Entry& 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 ) ; @@ -42,15 +42,19 @@ public : std::string Title() const ; std::string SelfHref() const ; + std::string ResourceID() const ; + const Resource* Parent() const ; Resource* Parent() ; std::string ParentHref() const ; - fs::path Dir() const ; + + fs::path Path() const ; bool IsInRootTree() const ; - std::string ResourceID() const ; + bool IsFolder() const ; + void AddChild( Resource *child ) ; - void AddLeaf( File *file ) ; + void AddLeaf( Resource *file ) ; void Swap( Resource& coll ) ; @@ -62,13 +66,20 @@ public : Resource* FindChild( const std::string& title ) ; void Update( const Entry& e ) ; + void Update( http::Agent *http, const http::Headers& auth ) ; + void Delete( http::Agent* http, const http::Headers& auth ) ; + +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 ; // not owned Resource *m_parent ; std::vector m_child ; - std::vector m_leaf ; + std::vector m_leaf ; } ; } // end of namespace diff --git a/libgrive/src/drive/State.cc b/libgrive/src/drive/State.cc index 512d8f6..dd31232 100644 --- a/libgrive/src/drive/State.cc +++ b/libgrive/src/drive/State.cc @@ -38,66 +38,6 @@ namespace gr { -namespace -{ -// struct Resource -// { -// std::string id ; -// fs::path path ; -// std::string md5sum ; -// std::time_t mtime ; -// -// explicit Resource( const fs::path& p ) : -// path( p ), -// md5sum( crypt::MD5( p ) ), -// mtime( fs::last_write_time( p ) ) -// { -// } -// -// explicit Resource( const Json& json ) : -// id( json["id"].Str() ), -// path( json["path"].Str() ), -// md5sum( json["md5"].Str() ), -// mtime( json["mtime"].Int() ) -// { -// } -// -// Json Get() const -// { -// Json entry ; -// entry.Add( "id", Json( id ) ) ; -// entry.Add( "path", Json( path.string() ) ) ; -// entry.Add( "md5", Json( md5sum ) ) ; -// entry.Add( "mtime", Json( mtime ) ) ; -// return entry ; -// } -// } ; -// -// struct PathHash -// { -// std::size_t operator()( const fs::path& p ) const -// { -// return boost::hash_value( p.string() ) ; -// } -// } ; -// -// using namespace boost::multi_index ; -// -// struct ByID {} ; -// struct ByPath {} ; -// -// typedef multi_index_container< -// Resource, -// indexed_by< -// hashed_non_unique< tag, member >, -// hashed_unique< tag,member, PathHash > -// > -// > ResourceSet ; -// -// typedef ResourceSet::index::type IDIdx ; -// typedef ResourceSet::index::type PathIdx ; -} - struct State::Impl { // ResourceSet rs ; @@ -117,13 +57,6 @@ State::State( const fs::path& filename ) : void State::Read( const fs::path& filename ) { Trace( "reading %1%", filename ) ; -// Json json = Json::ParseFile( filename.string() ) ; -// std::vector res = json["resources"].AsArray() ; -// -// for ( std::vector::iterator i = res.begin() ; i != res.end() ; ++i ) -// m_impl->rs.insert( Resource( *i ) ) ; -// -// m_impl->change_stamp = json["change_stamp"].Str() ; } std::string State::ChangeStamp() const @@ -145,15 +78,14 @@ void State::Sync( const boost::filesystem3::path& p, gr::Resource* folder ) { assert( folder != 0 ) ; -// Trace( "synchronizing = %1%", p ) ; for ( fs::directory_iterator i( p ) ; i != fs::directory_iterator() ; ++i ) { -// Trace( "file found = %2% (%1%)", i->path(), i->path().filename() ) ; if ( fs::is_directory( i->path() ) ) { Resource *c = new Resource( i->path().filename().string(), "" ) ; folder->AddChild( c ) ; - + m_impl->folders.Insert( c ) ; + Sync( *i, c ) ; } // else if ( i->path().filename().string()[0] != '.' ) @@ -166,42 +98,19 @@ void State::Write( const fs::path& filename ) const Json result ; result.Add( "change_stamp", Json( m_impl->change_stamp ) ) ; -// IDIdx& idx = m_impl->rs.get() ; -// -// std::vector res ; -// std::transform( idx.begin(), idx.end(), -// std::back_inserter(res), -// boost::bind( &Resource::Get, _1 ) ) ; -// -// result.Add( "resources", Json(res) ) ; - -// Trace( "%1%", result ) ; - std::ofstream fs( filename.string().c_str() ) ; fs << result ; } void State::SetId( const fs::path& p, const std::string& id ) { -// PathIdx& pidx = m_impl->rs.get() ; -// PathIdx::iterator it = pidx.find( p ) ; -// if ( it != pidx.end() ) -// { -// Resource r = *it ; -// r.id = id ; -// pidx.replace( it, r ) ; -// } -// else -// { -// Trace( "can't find %1%", p ) ; -// } } void State::OnEntry( const Entry& e ) { if ( !Update( e ) ) { - Trace( "can't find parent of %1%", e.Title() ) ; + Trace( "can't resolve folder %1%", e.Title() ) ; m_impl->unresolved.push_back( e ) ; } } @@ -230,6 +139,7 @@ std::size_t State::TryResolveEntry() for ( std::vector::iterator i = en.begin() ; i != en.end() ; ) { + Trace( "resolving %1%", i->Title() ) ; if ( Update( *i ) ) { i = en.erase( i ) ; @@ -261,7 +171,6 @@ bool State::Update( const Entry& e ) { child = new Resource( e ) ; parent->AddChild( child ) ; - m_impl->folders.Insert( child ) ; } return true ; }