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"
|
|
|
|
|
2012-05-16 18:35:22 +04:00
|
|
|
#include "util/Crypt.hh"
|
|
|
|
#include "util/Log.hh"
|
|
|
|
#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-05-19 13:14:04 +04:00
|
|
|
State::State( const fs::path& filename )
|
2012-05-16 20:52:17 +04:00
|
|
|
{
|
2012-05-20 13:12:01 +04:00
|
|
|
Read( filename ) ;
|
2012-05-16 20:52:17 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string State::ChangeStamp() const
|
|
|
|
{
|
2012-05-19 13:14:04 +04:00
|
|
|
return m_change_stamp ;
|
2012-05-16 20:52:17 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void State::ChangeStamp( const std::string& cs )
|
|
|
|
{
|
2012-05-19 13:14:04 +04:00
|
|
|
m_change_stamp = cs ;
|
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 ) ;
|
2012-05-19 14:03:02 +04:00
|
|
|
assert( folder->IsFolder() ) ;
|
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 )
|
|
|
|
{
|
2012-05-20 13:12:01 +04:00
|
|
|
std::string fname = i->path().filename().string() ;
|
|
|
|
|
|
|
|
if ( fname[0] == '.' )
|
|
|
|
Log( "file %1% is ignored by grive", fname, log::info ) ;
|
2012-05-20 11:25:38 +04:00
|
|
|
|
2012-05-19 21:44:46 +04:00
|
|
|
else
|
2012-05-19 14:03:02 +04:00
|
|
|
{
|
2012-05-20 19:16:59 +04:00
|
|
|
// if the Resource object of the child already exists, it should
|
|
|
|
// have been so no need to do anything here
|
2012-05-20 13:12:01 +04:00
|
|
|
Resource *c = folder->FindChild( fname ) ;
|
2012-05-20 15:28:44 +04:00
|
|
|
if ( c == 0 )
|
2012-05-20 13:12:01 +04:00
|
|
|
{
|
2012-05-20 19:16:59 +04:00
|
|
|
Log( "detected new file %1% in local", fname, log::verbose ) ;
|
2012-05-20 13:12:01 +04:00
|
|
|
c = new Resource( i->path() ) ;
|
|
|
|
folder->AddChild( c ) ;
|
|
|
|
m_res.Insert( c ) ;
|
|
|
|
}
|
|
|
|
|
2012-05-20 11:25:38 +04:00
|
|
|
if ( fs::is_directory( i->path() ) )
|
|
|
|
FromLocal( *i, c ) ;
|
2012-05-19 14:03:02 +04:00
|
|
|
}
|
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 ;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool State::Update( const Entry& e )
|
|
|
|
{
|
2012-05-19 14:27:53 +04:00
|
|
|
assert( !e.ParentHref().empty() ) ;
|
2012-05-19 14:03:02 +04:00
|
|
|
|
2012-05-20 15:28:44 +04:00
|
|
|
if ( Resource *res = m_res.FindByHref( e.SelfHref() ) )
|
2012-05-20 13:12:01 +04:00
|
|
|
{
|
2012-05-20 15:28:44 +04:00
|
|
|
m_res.Update( res, e ) ;
|
|
|
|
return true ;
|
2012-05-20 13:12:01 +04:00
|
|
|
}
|
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
|
|
|
{
|
2012-05-19 14:03:02 +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.
|
2012-05-20 11:25:38 +04:00
|
|
|
m_res.Update( child, e ) ;
|
2012-05-17 20:37:11 +04:00
|
|
|
}
|
|
|
|
|
2012-05-19 12:30:04 +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
|
|
|
{
|
2012-05-19 11:41:21 +04:00
|
|
|
child = new Resource( e ) ;
|
2012-05-19 11:26:55 +04:00
|
|
|
parent->AddChild( child ) ;
|
2012-05-20 11:25:38 +04:00
|
|
|
m_res.Insert( child ) ;
|
2012-05-19 12:30:04 +04:00
|
|
|
|
2012-05-19 14:03:02 +04:00
|
|
|
fs::path child_path = child->Path() ;
|
|
|
|
if ( child->IsFolder() && !fs::is_directory( child_path ) )
|
2012-05-19 13:14:04 +04:00
|
|
|
{
|
2012-05-19 14:03:02 +04:00
|
|
|
Log( "creating %1% directory", child_path, log::info ) ;
|
|
|
|
fs::create_directories( child_path ) ;
|
2012-05-19 13:14:04 +04:00
|
|
|
}
|
2012-05-17 20:37:11 +04:00
|
|
|
}
|
2012-05-20 10:57:25 +04:00
|
|
|
else
|
|
|
|
{
|
|
|
|
Trace( "what here? %1%", e.Title() ) ;
|
|
|
|
}
|
|
|
|
|
2012-05-17 20:37:11 +04:00
|
|
|
return true ;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return false ;
|
|
|
|
}
|
|
|
|
|
2012-05-19 11:41:21 +04:00
|
|
|
Resource* State::FindFolderByHref( 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
|
|
|
}
|
|
|
|
|
|
|
|
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 )
|
|
|
|
{
|
2012-05-20 13:12:01 +04:00
|
|
|
if ( fs::exists( filename ) )
|
|
|
|
{
|
|
|
|
Json json = Json::ParseFile( filename.string() ) ;
|
|
|
|
|
|
|
|
m_change_stamp = json["change_stamp"].Str() ;
|
|
|
|
m_res.Read( json["rtree"] ) ;
|
|
|
|
}
|
2012-05-20 11:25:38 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void State::Write( const fs::path& filename ) const
|
|
|
|
{
|
|
|
|
Json result ;
|
|
|
|
result.Add( "change_stamp", Json( m_change_stamp ) ) ;
|
2012-05-20 13:12:01 +04:00
|
|
|
result.Add( "rtree", m_res.Serialize() ) ;
|
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
|
|
|
|
2012-05-16 18:35:22 +04:00
|
|
|
} // end of namespace
|