saved the metadata of the files

pull/40/head
Matchman Green 2012-05-14 01:07:23 +08:00
parent 3c8c6bab54
commit 30f9834968
11 changed files with 281 additions and 125 deletions

View File

@ -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 )
{

View File

@ -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 )

View File

@ -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<Collection*> m_child ;
std::vector<std::string> m_leaves ;
std::vector<File*> m_leaf ;
} ;
} // end of namespace

View File

@ -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 <boost/bind.hpp>
// standard C++ library
#include <algorithm>
#include <cassert>
@ -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

View File

@ -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<File*> m_files ;
} ;
} // end of namespace

View File

@ -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 <algorithm>
#include <iterator>
#include <sstream>
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 =
"<?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_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_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 )

View File

@ -19,12 +19,11 @@
#pragma once
#include <string>
#include "http/Agent.hh"
#include "util/DateTime.hh"
#include <iosfwd>
#include <string>
#include <vector>
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<std::string>& 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 :

148
libgrive/src/drive/File.cc Normal file
View File

@ -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 <cassert>
#include <fstream>
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 =
"<?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() ) ;
return true ;
}
} // end of namespace

View File

@ -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 <iosfwd>
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

View File

@ -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 ;

View File

@ -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 <typename T>
void operator()( T *t ) const
{
delete t ;
}
} ;
} // end of namespace