diff --git a/CMakeLists.txt b/CMakeLists.txt index e345418..f86b762 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,7 @@ add_executable( unittest test/UnitTest.cc src/util/DateTime.cc test/util/DateTimeTest.cc + test/util/FunctionTest.cc ) target_link_libraries( unittest diff --git a/src/drive/Collection.cc b/src/drive/Collection.cc index 8fc5ce5..ab8f8ea 100644 --- a/src/drive/Collection.cc +++ b/src/drive/Collection.cc @@ -82,6 +82,11 @@ void Collection::AddChild( Collection *child ) m_child.push_back( child ) ; } +void Collection::AddLeaf( const std::string& filename ) +{ + m_leaves.push_back( filename ) ; +} + bool Collection::IsCollection( const Json& entry ) { Json node ; @@ -96,12 +101,12 @@ void Collection::Swap( Collection& coll ) m_href.swap( coll.m_href ) ; std::swap( m_parent, coll.m_parent ) ; m_child.swap( coll.m_child ) ; + m_leaves.swap( coll.m_leaves ) ; } void Collection::CreateSubDir( const std::string& prefix ) { std::string dir = prefix + m_title ; -// mkdir( dir.c_str(), 0700 ) ; os::MakeDir( dir ) ; for ( std::vector::iterator i = m_child.begin() ; i != m_child.end() ; ++i ) @@ -111,6 +116,12 @@ void Collection::CreateSubDir( const std::string& prefix ) } } +void Collection::ForEachFile( + Function callback, + const std::string& prefix ) +{ +} + std::string Collection::Path() const { assert( m_parent != this ) ; diff --git a/src/drive/Collection.hh b/src/drive/Collection.hh index c84e512..9ff777a 100644 --- a/src/drive/Collection.hh +++ b/src/drive/Collection.hh @@ -19,6 +19,8 @@ #pragma once +#include "util/Function.hh" + #include #include @@ -44,10 +46,15 @@ public : std::string Path() const ; void AddChild( Collection *child ) ; + void AddLeaf( const std::string& filename ) ; void Swap( Collection& coll ) ; - void CreateSubDir( const std::string& prefix ) ; + // traversing the tree + void CreateSubDir( const std::string& prefix = "." ) ; + void ForEachFile( + Function callback, + const std::string& prefix = "." ) ; private : std::string m_title ; @@ -56,6 +63,8 @@ private : // not owned Collection *m_parent ; std::vector m_child ; + + std::vector m_leaves ; } ; } // end of namespace diff --git a/src/drive/Drive.cc b/src/drive/Drive.cc index 91641c1..37e4eb1 100644 --- a/src/drive/Drive.cc +++ b/src/drive/Drive.cc @@ -185,8 +185,6 @@ void Drive::UpdateFile( const Json& entry ) std::string filename = entry["docs$filename"]["$t"].As() ; std::string url = entry["content"]["src"].As() ; std::string parent_href = Parent( entry ) ; - - DateTime remote( entry["updated"]["$t"].As() ) ; bool changed = true ; std::string path = "./" + filename ; @@ -198,16 +196,13 @@ void Drive::UpdateFile( const Json& entry ) if ( pit != m_coll.end() ) path = pit->Path() + "/" + filename ; } - DateTime local = os::FileMTime( path ) ; - std::cout << "file time: " << entry["updated"]["$t"].As() << " " << remote << " " << local << std::endl ; +// std::cout << "file time: " << entry["updated"]["$t"].As() << " " << remote << " " << local << std::endl ; // compare checksum first if file exists std::ifstream ifile( path.c_str(), std::ios::binary | std::ios::out ) ; if ( ifile && entry.Has("docs$md5Checksum") ) { - os::SetFileTime( path, remote ) ; - std::string remote_md5 = entry["docs$md5Checksum"]["$t"].As() ; std::string local_md5 = MD5( ifile.rdbuf() ) ; @@ -216,13 +211,39 @@ void Drive::UpdateFile( const Json& entry ) changed = false ; } - // if the checksum is different, file is changed and we need to download + // if the checksum is different, file is changed and we need to update if ( changed ) { + DateTime remote( entry["updated"]["$t"].As() ) ; + DateTime local = ifile ? os::FileMTime( path ) : DateTime() ; + + // remote file is newer, download file + if ( remote > local ) + { std::cout << "downloading " << path << std::endl ; - HttpGetFile( url, path, m_http_hdr ) ; + HttpGetFile( url, path, m_http_hdr ) ; + os::SetFileTime( path, remote ) ; + } + else + { +std::cout << "local " << filename << " is newer" << std::endl ; + UploadFile( entry ) ; + } } } } +void Drive::UploadFile( const Json& entry ) +{ +// std::cout << "entry:\n" << entry << std::endl ; + + Json resume_link = entry["link"].FindInArray( "rel", + "http://schemas.google.com/g/2005#resumable-edit-media" )["href"] ; + std::cout << resume_link.As() << std::endl ; + + std::string resp = HttpPut( resume_link.As(), "" ) ; + + std::cout << "resp " << resp ; +} + } // end of namespace diff --git a/src/drive/Drive.hh b/src/drive/Drive.hh index 196ec24..768869c 100644 --- a/src/drive/Drive.hh +++ b/src/drive/Drive.hh @@ -41,6 +41,7 @@ public : private : void UpdateFile( const Json& entry ) ; + void UploadFile( const Json& entry ) ; std::string Parent( const Json& entry ) ; void ConstructDirTree( const std::vector& entries ) ; diff --git a/src/protocol/HTTP.cc b/src/protocol/HTTP.cc index 1965649..ed38cdb 100644 --- a/src/protocol/HTTP.cc +++ b/src/protocol/HTTP.cc @@ -24,7 +24,9 @@ // dependent libraries #include +#include #include +#include #include #include #include @@ -54,6 +56,20 @@ std::size_t WriteCallback( char *data, size_t size, size_t nmemb, std::string *r return count ; } +size_t ReadCallback( void *ptr, std::size_t size, std::size_t nmemb, std::string *data ) +{ + assert( ptr != 0 ) ; + assert( data != 0 ) ; + + std::size_t count = std::min( size * nmemb, data->size() ) ; + if ( count > 0 ) + { + std::memcpy( &(*data)[0], ptr, count ) ; + data->erase( 0, count ) ; + } + return count ; +} + CURL* InitCurl( const std::string& url, std::string *resp, const Headers& hdr ) { CURL *curl = curl_easy_init(); @@ -154,6 +170,25 @@ std::string HttpPostFile( const std::string& url, const std::string& filename, c return resp; } +std::string HttpPut( + const std::string& url, + const std::string& data, + const Headers& hdr ) +{ + std::string resp ; + CURL *curl = InitCurl( url, &resp, hdr ) ; + + std::string put_data = data ; + + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1); + curl_easy_setopt(curl, CURLOPT_READFUNCTION, &ReadCallback ) ; + curl_easy_setopt(curl, CURLOPT_READDATA , &put_data ) ; + curl_easy_setopt(curl, CURLOPT_INFILESIZE, put_data.size() ) ; + + DoCurl( curl ) ; + return resp; +} + std::string Escape( const std::string& str ) { CURL *curl = curl_easy_init(); diff --git a/src/protocol/HTTP.hh b/src/protocol/HTTP.hh index f399a64..1b7ab05 100644 --- a/src/protocol/HTTP.hh +++ b/src/protocol/HTTP.hh @@ -48,6 +48,10 @@ namespace gr const std::string& filename, const Headers& hdr = Headers() ) ; + std::string HttpPut( + const std::string& url, + const Headers& hdr = Headers() ) ; + std::string Escape( const std::string& str ) ; std::string Unescape( const std::string& str ) ; diff --git a/src/util/DateTime.cc b/src/util/DateTime.cc index b32111b..76e553e 100644 --- a/src/util/DateTime.cc +++ b/src/util/DateTime.cc @@ -19,6 +19,7 @@ #include "DateTime.hh" +#include #include #include #include @@ -53,8 +54,8 @@ DateTime::DateTime( const std::string& iso ) : } DateTime::DateTime( std::time_t sec, unsigned long nsec ) : - m_sec ( sec ), - m_nsec ( nsec ) + m_sec ( sec + nsec / 1000000000 ), + m_nsec ( nsec % 1000000000 ) { } @@ -72,6 +73,7 @@ std::time_t DateTime::Sec( ) const unsigned long DateTime::NanoSec( ) const { + assert( m_nsec < 1000000000 ) ; return m_nsec ; } @@ -86,10 +88,45 @@ std::ostream& operator<<( std::ostream& os, const DateTime& dt ) struct timeval DateTime::Tv() const { + assert( m_nsec < 1000000000 ) ; + timeval result ; result.tv_sec = m_sec ; result.tv_usec = m_nsec / 1000 ; return result ; } +bool DateTime::operator==( const DateTime& dt ) const +{ + assert( m_nsec < 1000000000 ) ; + return m_sec == dt.m_sec && m_nsec == dt.m_nsec ; +} + +bool DateTime::operator!=( const DateTime& dt ) const +{ + return !( *this == dt ) ; +} + +bool DateTime::operator>( const DateTime& dt ) const +{ + assert( m_nsec < 1000000000 ) ; + assert( dt.m_nsec < 1000000000 ) ; + return m_sec == dt.m_sec ? m_nsec > dt.m_nsec : m_sec > dt.m_sec ; +} + +bool DateTime::operator>=( const DateTime& dt ) const +{ + return ( *this > dt ) || ( *this == dt ) ; +} + +bool DateTime::operator<( const DateTime& dt ) const +{ + return !( *this >= dt ) ; +} + +bool DateTime::operator<=( const DateTime& dt ) const +{ + return !( *this > dt ) ; +} + } // end of namespace diff --git a/src/util/DateTime.hh b/src/util/DateTime.hh index a2e1ae4..ff89f74 100644 --- a/src/util/DateTime.hh +++ b/src/util/DateTime.hh @@ -38,6 +38,13 @@ public : struct tm Tm() const ; struct timeval Tv() const ; + bool operator==( const DateTime& dt ) const ; + bool operator!=( const DateTime& dt ) const ; + bool operator>( const DateTime& dt ) const ; + bool operator>=( const DateTime& dt ) const ; + bool operator<( const DateTime& dt ) const ; + bool operator<=( const DateTime& dt ) const ; + private : std::time_t m_sec ; unsigned long m_nsec ; diff --git a/src/util/Function.hh b/src/util/Function.hh new file mode 100644 index 0000000..197418f --- /dev/null +++ b/src/util/Function.hh @@ -0,0 +1,184 @@ +/* + 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 +#include + +namespace gr { + +namespace impl +{ + template class FuncImpl ; + + template class FuncImpl + { + public : + virtual R operator()( ) = 0 ; + virtual FuncImpl* Clone() const = 0 ; + virtual ~FuncImpl() {} + } ; + + template class FuncImpl + { + public : + virtual R operator()(P1) = 0 ; + virtual FuncImpl* Clone() const = 0 ; + virtual ~FuncImpl() {} + } ; + + template class FuncImpl + { + public : + virtual R operator()(P1,P2) = 0 ; + virtual FuncImpl* Clone() const = 0 ; + virtual ~FuncImpl() {} + } ; + + template + struct FuncTrait ; + + struct NullType {} ; + + template struct FuncTrait + { + typedef R ReturnType ; + typedef NullType Param1Type ; + typedef NullType Param2Type ; + } ; + + template struct FuncTrait + { + typedef R ReturnType ; + typedef P1 Param1Type ; + typedef NullType Param2Type ; + } ; + + template struct FuncTrait + { + typedef R ReturnType ; + typedef P1 Param1Type ; + typedef P2 Param2Type ; + } ; + + template class FuncHolder : public FuncImpl + { + public : + explicit FuncHolder( const F& f ) : m_func( f ) { } + FuncHolder* Clone() const { return new FuncHolder( *this ) ; } + + typedef typename FuncTrait::ReturnType ReturnType ; + + ReturnType operator()() { return m_func(); } + + ReturnType operator()( typename FuncTrait::Param1Type p1) + { + return m_func(p1); + } + + ReturnType operator()( + typename FuncTrait::Param1Type p1, + typename FuncTrait::Param2Type p2) + { + return m_func(p1,p2); + } + + private : + F m_func ; + } ; + + template + struct NullFunc + { + typedef typename FuncTrait::ReturnType ReturnType ; + ReturnType operator()() + { + return ReturnType() ; + } + ReturnType operator()( typename FuncTrait::Param1Type p1) + { + return ReturnType() ; + } + ReturnType operator()( + typename FuncTrait::Param1Type p1, + typename FuncTrait::Param2Type p2) + { + return ReturnType() ; + } + } ; + + template + FuncHolder* MakeFuncHolder( F func ) + { + return new FuncHolder( func ) ; + } +} + +template +class Function +{ +public : + Function( ) : + m_pimpl( impl::MakeFuncHolder( impl::NullFunc() ) ) + { + } + Function( const Function& f ) : + m_pimpl( f.m_pimpl->Clone() ) + { + } + template + Function( const F& f ) : + m_pimpl( impl::MakeFuncHolder( f ) ) + { + } + + Function& operator=( const Function& f ) + { + Function tmp( f ) ; + std::swap( m_pimpl, tmp.m_pimpl ) ; + return *this ; + } + ~Function( ) + { + } + + typedef typename impl::FuncTrait::ReturnType ReturnType ; + + ReturnType operator()( ) + { + return (*m_pimpl)() ; + } + + template ReturnType operator()( P1 p1 ) + { + return (*m_pimpl)( p1 ) ; + } + + template ReturnType operator()( P1 p1, P2 p2 ) + { + return (*m_pimpl)( p1, p2 ) ; + } ; + +private : + typedef impl::FuncImpl Impl ; + std::auto_ptr m_pimpl ; +} ; + +} // end of namespace diff --git a/test/util/DateTimeTest.cc b/test/util/DateTimeTest.cc index 6c803f4..341c3a8 100644 --- a/test/util/DateTimeTest.cc +++ b/test/util/DateTimeTest.cc @@ -60,4 +60,26 @@ void DateTimeTest::TestOffByOne( ) CPPUNIT_ASSERT_EQUAL( 21, tp.tm_mday ) ; } +void DateTimeTest::TestCompare( ) +{ + DateTime s1( 1000, 2000 ), s2( 1001, 2000 ), s3( 1000, 2001 ), s4( 1001, 2000 ) ; + + CPPUNIT_ASSERT( s1 < s3 ) ; + CPPUNIT_ASSERT( s1 <= s3 ) ; + CPPUNIT_ASSERT( s3 > s1 ) ; + CPPUNIT_ASSERT( s3 >= s1 ) ; + + CPPUNIT_ASSERT( s1 < s2 ) ; + CPPUNIT_ASSERT( s1 <= s2 ) ; + CPPUNIT_ASSERT( s2 > s1 ) ; + CPPUNIT_ASSERT( s2 >= s1 ) ; + + CPPUNIT_ASSERT( s2 == s4 ) ; + CPPUNIT_ASSERT( s2 >= s4 ) ; + CPPUNIT_ASSERT( s2 <= s4 ) ; + CPPUNIT_ASSERT( s4 == s2 ) ; + CPPUNIT_ASSERT( s4 >= s2 ) ; + CPPUNIT_ASSERT( s4 <= s2 ) ; +} + } // end of namespace grut diff --git a/test/util/DateTimeTest.hh b/test/util/DateTimeTest.hh index 1246a33..0c12a6d 100644 --- a/test/util/DateTimeTest.hh +++ b/test/util/DateTimeTest.hh @@ -35,6 +35,7 @@ public : CPPUNIT_TEST( TestParseNoMillisec ) ; CPPUNIT_TEST( TestOffByOne ) ; CPPUNIT_TEST( TestParseInvalid ) ; + CPPUNIT_TEST( TestCompare ) ; CPPUNIT_TEST_SUITE_END(); private : @@ -42,6 +43,7 @@ private : void TestParseNoMillisec( ) ; void TestOffByOne( ) ; void TestParseInvalid( ) ; + void TestCompare( ) ; } ; } // end of namespace diff --git a/test/util/FunctionTest.cc b/test/util/FunctionTest.cc new file mode 100644 index 0000000..2b24d2d --- /dev/null +++ b/test/util/FunctionTest.cc @@ -0,0 +1,46 @@ +/* + 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 "FunctionTest.hh" + +#include "util/Function.hh" + +namespace grut { + +using namespace gr ; + +FunctionTest::FunctionTest( ) +{ +} + +int TestFunction( int v ) +{ + return v ; +} + +void FunctionTest::TestRun( ) +{ + Function f = &TestFunction ; + Function f2 ; + + CPPUNIT_ASSERT_EQUAL( 3, f(3) ) ; + CPPUNIT_ASSERT_EQUAL( std::string(), f2() ) ; +} + +} // end of namespace grut diff --git a/test/util/FunctionTest.hh b/test/util/FunctionTest.hh new file mode 100644 index 0000000..9e05979 --- /dev/null +++ b/test/util/FunctionTest.hh @@ -0,0 +1,41 @@ +/* + 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 +#include + +namespace grut { + +class FunctionTest : public CppUnit::TestFixture +{ +public : + FunctionTest( ) ; + + // declare suit function + CPPUNIT_TEST_SUITE( FunctionTest ) ; + CPPUNIT_TEST( TestRun ) ; + CPPUNIT_TEST_SUITE_END(); + +private : + void TestRun( ) ; +} ; + +} // end of namespace