grive2/libgrive/src/drive/State.cc

260 lines
5.8 KiB
C++
Raw Normal View History

2012-05-16 18:35:22 +04:00
/*
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 "State.hh"
2012-05-19 11:41:21 +04:00
#include "Resource.hh"
2012-05-17 20:37:11 +04:00
#include "CommonUri.hh"
#include "http/Agent.hh"
2012-05-16 18:35:22 +04:00
#include "util/Crypt.hh"
2012-06-03 14:31:02 +04:00
#include "util/log/Log.hh"
2012-05-16 18:35:22 +04:00
#include "protocol/Json.hh"
2012-05-16 20:52:17 +04:00
#include <boost/bind.hpp>
2012-05-16 18:35:22 +04:00
2012-05-16 20:52:17 +04:00
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>
2012-05-17 20:37:11 +04:00
#include <boost/multi_index/mem_fun.hpp>
2012-05-16 18:35:22 +04:00
2012-05-16 20:52:17 +04:00
#include <fstream>
2012-05-16 18:35:22 +04:00
2012-05-16 20:52:17 +04:00
namespace gr {
2012-06-04 20:59:14 +04:00
State::State( const fs::path& filename, const Json& options ) :
m_cstamp( -1 )
2012-05-16 20:52:17 +04:00
{
Read( filename ) ;
2012-05-31 19:37:47 +04:00
// the "-f" option will make grive always thinks remote is newer
Json force ;
if ( options.Get("force", force) && force.Bool() )
m_last_sync = DateTime() ;
2012-06-03 19:58:28 +04:00
Log( "last sync time: %1%", m_last_sync, log::info ) ;
2012-05-16 20:52:17 +04:00
}
2012-05-19 21:44:46 +04:00
/// Synchronize local directory. Build up the resource tree from files and folders
/// of local directory.
void State::FromLocal( const fs::path& p )
2012-05-16 20:52:17 +04:00
{
2012-05-20 11:25:38 +04:00
FromLocal( p, m_res.Root() ) ;
2012-05-17 20:37:11 +04:00
}
2012-05-19 21:44:46 +04:00
void State::FromLocal( const fs::path& p, gr::Resource* folder )
2012-05-17 20:37:11 +04:00
{
2012-05-19 11:26:55 +04:00
assert( folder != 0 ) ;
assert( folder->IsFolder() ) ;
// sync the folder itself
folder->FromLocal( m_last_sync ) ;
2012-05-19 11:26:55 +04:00
2012-05-16 20:52:17 +04:00
for ( fs::directory_iterator i( p ) ; i != fs::directory_iterator() ; ++i )
{
std::string fname = i->path().filename().string() ;
if ( fname[0] == '.' )
Log( "file %1% is ignored by grive", fname, log::verbose ) ;
2012-05-20 11:25:38 +04:00
2012-05-19 21:44:46 +04:00
else
{
// if the Resource object of the child already exists, it should
// have been so no need to do anything here
Resource *c = folder->FindChild( fname ) ;
2012-05-20 15:28:44 +04:00
if ( c == 0 )
{
2012-05-31 19:37:47 +04:00
c = new Resource( fname, fs::is_directory(i->path()) ? "folder" : "file" ) ;
folder->AddChild( c ) ;
m_res.Insert( c ) ;
}
c->FromLocal( m_last_sync ) ;
2012-05-20 11:25:38 +04:00
if ( fs::is_directory( i->path() ) )
FromLocal( *i, c ) ;
}
2012-05-16 18:35:22 +04:00
}
}
2012-05-19 21:44:46 +04:00
void State::FromRemote( const Entry& e )
2012-05-17 20:37:11 +04:00
{
if ( !Update( e ) )
{
2012-05-19 13:14:04 +04:00
m_unresolved.push_back( e ) ;
2012-05-17 20:37:11 +04:00
}
}
void State::ResolveEntry()
{
2012-05-19 13:14:04 +04:00
while ( !m_unresolved.empty() )
2012-05-17 20:37:11 +04:00
{
if ( TryResolveEntry() == 0 )
break ;
}
}
std::size_t State::TryResolveEntry()
{
2012-05-19 13:14:04 +04:00
assert( !m_unresolved.empty() ) ;
2012-05-17 20:37:11 +04:00
std::size_t count = 0 ;
2012-05-19 13:14:04 +04:00
std::vector<Entry>& en = m_unresolved ;
2012-05-17 20:37:11 +04:00
for ( std::vector<Entry>::iterator i = en.begin() ; i != en.end() ; )
{
if ( Update( *i ) )
{
i = en.erase( i ) ;
count++ ;
}
else
++i ;
}
return count ;
}
void State::FromChange( const Entry& e )
{
// entries in the change feed is always treated as remote newer,
// so we override the last sync time to 0
if ( Resource *res = m_res.FindByHref( e.AltSelf() ) )
{
Log( "found %1% in change %2%", res->Name(), e.ChangeStamp() ) ;
m_res.Update( res, e, DateTime() ) ;
}
else
Log( "can't found %1% %2%", e.Filename(), e.SelfHref() ) ;
}
2012-05-17 20:37:11 +04:00
bool State::Update( const Entry& e )
{
2012-05-19 14:27:53 +04:00
assert( !e.ParentHref().empty() ) ;
2012-06-03 11:55:32 +04:00
2012-05-20 15:28:44 +04:00
if ( Resource *res = m_res.FindByHref( e.SelfHref() ) )
{
m_res.Update( res, e, m_last_sync ) ;
2012-05-20 15:28:44 +04:00
return true ;
}
2012-05-20 15:28:44 +04:00
else if ( Resource *parent = m_res.FindByHref( e.ParentHref() ) )
2012-05-17 20:37:11 +04:00
{
assert( parent->IsFolder() ) ;
2012-05-20 10:57:25 +04:00
2012-05-17 20:37:11 +04:00
// see if the entry already exist in local
2012-05-20 10:57:25 +04:00
std::string name = ( e.Kind() == "folder" ? e.Title() : e.Filename() ) ;
Resource *child = parent->FindChild( name ) ;
2012-05-17 20:37:11 +04:00
if ( child != 0 )
{
// since we are updating the ID and Href, we need to remove it and re-add it.
m_res.Update( child, e, m_last_sync ) ;
2012-05-17 20:37:11 +04:00
}
// folder entry exist in google drive, but not local. we should create
// the directory
2012-05-19 13:14:04 +04:00
else if ( e.Kind() == "folder" || !e.Filename().empty() )
2012-05-17 20:37:11 +04:00
{
// first create a dummy resource and update it later
2012-05-31 19:37:47 +04:00
child = new Resource( name, e.Kind() ) ;
parent->AddChild( child ) ;
m_res.Insert( child ) ;
// update the state of the resource
m_res.Update( child, e, m_last_sync ) ;
2012-05-17 20:37:11 +04:00
}
2012-05-20 10:57:25 +04:00
2012-05-17 20:37:11 +04:00
return true ;
}
else
return false ;
}
Resource* State::FindByHref( const std::string& href )
2012-05-17 20:37:11 +04:00
{
2012-05-20 11:25:38 +04:00
return m_res.FindByHref( href ) ;
2012-05-19 13:14:04 +04:00
}
Resource* State::Find( const fs::path& path )
{
return m_res.FindByPath( path ) ;
}
2012-05-19 13:14:04 +04:00
State::iterator State::begin()
{
2012-05-20 11:25:38 +04:00
return m_res.begin() ;
2012-05-19 13:14:04 +04:00
}
State::iterator State::end()
{
2012-05-20 11:25:38 +04:00
return m_res.end() ;
}
void State::Read( const fs::path& filename )
{
try
{
Json json = Json::ParseFile( filename.string() ) ;
Json last_sync = json["last_sync"] ;
m_last_sync.Assign(
last_sync["sec"].Int(),
last_sync["nsec"].Int() ) ;
2012-06-03 19:58:28 +04:00
m_cstamp = json["change_stamp"].Int() ;
}
catch ( Exception& )
{
m_last_sync.Assign(0) ;
}
2012-05-20 11:25:38 +04:00
}
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() ) );
2012-05-20 11:25:38 +04:00
Json result ;
result.Add( "last_sync", last_sync ) ;
2012-06-03 19:58:28 +04:00
result.Add( "change_stamp", Json(m_cstamp) ) ;
2012-05-20 11:25:38 +04:00
std::ofstream fs( filename.string().c_str() ) ;
fs << result ;
2012-05-17 20:37:11 +04:00
}
2012-05-16 21:20:02 +04:00
void State::Sync( http::Agent *http, const http::Header& auth )
{
std::for_each( m_res.begin(), m_res.end(),
boost::bind( &Resource::Sync, _1, http, auth ) ) ;
m_last_sync = DateTime::Now() ;
}
2012-06-03 19:58:28 +04:00
long State::ChangeStamp() const
{
return m_cstamp ;
}
void State::ChangeStamp( long cstamp )
{
m_cstamp = cstamp ;
}
2012-05-16 18:35:22 +04:00
} // end of namespace