2005-11-26 22:16:03 +01:00
/*
2006-12-10 18:35:20 +01:00
* Copyright ( C ) 2005 - 2006 Patrick Ohly
* Copyright ( C ) 2007 Funambol
2005-11-26 22:16:03 +01:00
*
* 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 ; either version 2 of the License , or
* ( at your option ) any later version .
*
* 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 . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include "EvolutionSyncClient.h"
# include "EvolutionSyncSource.h"
2008-03-28 23:31:21 +01:00
# include "SyncEvolutionUtil.h"
2005-11-26 22:16:03 +01:00
2006-03-19 22:37:30 +01:00
# include <posix/base/posixlog.h>
2005-11-26 22:16:03 +01:00
# include <list>
# include <memory>
# include <vector>
2006-03-19 22:37:30 +01:00
# include <sstream>
# include <fstream>
# include <iomanip>
# include <iostream>
2006-05-26 14:49:19 +02:00
# include <stdexcept>
2005-11-26 22:16:03 +01:00
using namespace std ;
2008-03-30 21:08:19 +02:00
# include <boost/algorithm/string/predicate.hpp>
2006-03-19 22:37:30 +01:00
# include <sys/stat.h>
# include <pwd.h>
# include <unistd.h>
# include <dirent.h>
# include <errno.h>
2007-10-11 23:02:49 +02:00
SourceList * EvolutionSyncClient : : m_sourceListPtr ;
2006-09-29 01:02:43 +02:00
EvolutionSyncClient : : EvolutionSyncClient ( const string & server ,
2008-03-06 23:23:13 +01:00
bool doLogging ,
const set < string > & sources ) :
EvolutionSyncConfig ( server ) ,
2005-11-26 22:16:03 +01:00
m_server ( server ) ,
2006-04-09 13:48:11 +02:00
m_sources ( sources ) ,
2006-08-06 09:56:41 +02:00
m_doLogging ( doLogging ) ,
2008-03-24 22:42:47 +01:00
m_syncMode ( SYNC_NONE ) ,
m_quiet ( false )
2005-11-26 22:16:03 +01:00
{
}
EvolutionSyncClient : : ~ EvolutionSyncClient ( )
{
}
2006-03-19 22:37:30 +01:00
// this class owns the logging directory and is responsible
// for redirecting output at the start and end of sync (even
// in case of exceptions thrown!)
class LogDir {
string m_logdir ; /**< configured backup root dir, empty if none */
int m_maxlogdirs ; /**< number of backup dirs to preserve, 0 if unlimited */
string m_prefix ; /**< common prefix of backup dirs */
string m_path ; /**< path to current logging and backup dir */
2007-04-21 14:33:23 +02:00
string m_logfile ; /**< path to log file there, empty if not writing one */
2006-03-19 22:37:30 +01:00
const string & m_server ; /**< name of the server for this synchronization */
2006-04-06 19:02:43 +02:00
LogLevel m_oldLogLevel ; /**< logging level to restore */
2007-03-31 18:22:40 +02:00
bool m_restoreLog ; /**< false if nothing needs to be restored because setLogdir() was never called */
2006-03-19 22:37:30 +01:00
public :
2006-04-06 19:02:43 +02:00
LogDir ( const string & server ) : m_server ( server ) ,
2007-03-31 18:22:40 +02:00
m_restoreLog ( false )
2007-11-08 22:22:52 +01:00
{
// SyncEvolution-<server>-<yyyy>-<mm>-<dd>-<hh>-<mm>
m_prefix = " SyncEvolution- " ;
m_prefix + = m_server ;
// default: $TMPDIR/SyncEvolution-<username>-<server>
stringstream path ;
char * tmp = getenv ( " TMPDIR " ) ;
if ( tmp ) {
path < < tmp ;
} else {
path < < " /tmp " ;
}
path < < " /SyncEvolution- " ;
struct passwd * user = getpwuid ( getuid ( ) ) ;
if ( user & & user - > pw_name ) {
path < < user - > pw_name ;
} else {
path < < getuid ( ) ;
}
path < < " - " < < m_server ;
m_path = path . str ( ) ;
}
/**
* Finds previous log directory . Must be called before setLogdir ( ) .
*
* @ param path path to configured backup directy , NULL if defaulting to / tmp , " none " if not creating log file
* @ return full path of previous log directory , empty string if not found
*/
string previousLogdir ( const char * path ) {
string logdir ;
if ( path & & ! strcasecmp ( path , " none " ) ) {
return " " ;
} else if ( path & & path [ 0 ] ) {
vector < string > entries ;
try {
getLogdirs ( path , entries ) ;
} catch ( const std : : exception & ex ) {
LOG . error ( " %s " , ex . what ( ) ) ;
return " " ;
}
logdir = entries . size ( ) ? string ( path ) + " / " + entries [ entries . size ( ) - 1 ] : " " ;
} else {
logdir = m_path ;
}
if ( access ( logdir . c_str ( ) , R_OK | X_OK ) = = 0 ) {
return logdir ;
} else {
return " " ;
}
}
2006-03-19 22:37:30 +01:00
// setup log directory and redirect logging into it
2007-04-21 14:33:23 +02:00
// @param path path to configured backup directy, NULL if defaulting to /tmp, "none" if not creating log file
2006-03-19 22:37:30 +01:00
// @param maxlogdirs number of backup dirs to preserve in path, 0 if unlimited
2007-04-21 14:33:23 +02:00
// @param logLevel 0 = default, 1 = ERROR, 2 = INFO, 3 = DEBUG
void setLogdir ( const char * path , int maxlogdirs , int logLevel = 0 ) {
2006-03-19 22:37:30 +01:00
m_maxlogdirs = maxlogdirs ;
2007-04-21 14:33:23 +02:00
if ( path & & ! strcasecmp ( path , " none " ) ) {
m_logfile = " " ;
} else if ( path & & path [ 0 ] ) {
2006-03-19 22:37:30 +01:00
m_logdir = path ;
// create unique directory name in the given directory
time_t ts = time ( NULL ) ;
struct tm * tm = localtime ( & ts ) ;
stringstream base ;
base < < path < < " / "
< < m_prefix
< < " - "
< < setfill ( ' 0 ' )
< < setw ( 4 ) < < tm - > tm_year + 1900 < < " - "
2007-04-21 14:33:23 +02:00
< < setw ( 2 ) < < tm - > tm_mon + 1 < < " - "
2006-06-19 18:23:07 +02:00
< < setw ( 2 ) < < tm - > tm_mday < < " - "
< < setw ( 2 ) < < tm - > tm_hour < < " - "
< < setw ( 2 ) < < tm - > tm_min ;
2006-03-19 22:37:30 +01:00
int seq = 0 ;
while ( true ) {
stringstream path ;
path < < base . str ( ) ;
if ( seq ) {
path < < " - " < < seq ;
}
m_path = path . str ( ) ;
if ( ! mkdir ( m_path . c_str ( ) , S_IRWXU ) ) {
break ;
}
if ( errno ! = EEXIST ) {
2007-10-07 21:06:01 +02:00
LOG . debug ( " %s: %s " , m_path . c_str ( ) , strerror ( errno ) ) ;
2007-10-11 23:02:49 +02:00
EvolutionSyncClient : : throwError ( m_path + " : " + strerror ( errno ) ) ;
2006-03-19 22:37:30 +01:00
}
seq + + ;
}
2007-04-21 14:33:23 +02:00
m_logfile = m_path + " /client.log " ;
2006-03-19 22:37:30 +01:00
} else {
2007-11-08 22:22:52 +01:00
// use the default temp directory
2006-03-19 22:37:30 +01:00
if ( mkdir ( m_path . c_str ( ) , S_IRWXU ) ) {
if ( errno ! = EEXIST ) {
2007-10-11 23:02:49 +02:00
EvolutionSyncClient : : throwError ( m_path + " : " + strerror ( errno ) ) ;
2006-03-19 22:37:30 +01:00
}
}
2007-04-21 14:33:23 +02:00
m_logfile = m_path + " /client.log " ;
2006-03-19 22:37:30 +01:00
}
2007-04-21 14:33:23 +02:00
if ( m_logfile . size ( ) ) {
// redirect logging into that directory, including stderr,
// after truncating it
2007-10-07 21:06:01 +02:00
FILE * file = fopen ( m_logfile . c_str ( ) , " w " ) ;
if ( file ) {
fclose ( file ) ;
2008-01-12 23:53:50 +01:00
# ifdef POSIX_LOG
POSIX_LOG .
# endif
setLogFile ( NULL , m_logfile . c_str ( ) , true ) ;
2007-10-07 21:06:01 +02:00
} else {
LOG . error ( " creating log file %s failed " , m_logfile . c_str ( ) ) ;
}
2007-04-21 14:33:23 +02:00
}
2007-03-31 18:22:40 +02:00
m_oldLogLevel = LOG . getLevel ( ) ;
2007-04-21 14:33:23 +02:00
LOG . setLevel ( logLevel > 0 ? ( LogLevel ) ( logLevel - 1 ) /* fixed level */ :
m_logfile . size ( ) ? LOG_LEVEL_DEBUG /* default for log file */ :
LOG_LEVEL_INFO /* default for console output */ ) ;
2007-03-31 18:22:40 +02:00
m_restoreLog = true ;
2006-03-19 22:37:30 +01:00
}
2007-11-08 22:22:52 +01:00
/** sets a fixed directory for database files without redirecting logging */
void setPath ( const string & path ) { m_path = path ; }
2006-03-19 22:37:30 +01:00
// return log directory, empty if not enabled
const string & getLogdir ( ) {
return m_path ;
}
// return log file, empty if not enabled
const string & getLogfile ( ) {
return m_logfile ;
}
2007-11-08 22:22:52 +01:00
/** find all entries in a given directory, return as sorted array */
void getLogdirs ( const string & logdir , vector < string > & entries ) {
2008-03-28 23:31:21 +01:00
ReadDir dir ( logdir ) ;
for ( ReadDir : : const_iterator it = dir . begin ( ) ;
it ! = dir . end ( ) ;
+ + it ) {
2008-03-30 21:08:19 +02:00
if ( boost : : starts_with ( * it , m_prefix ) ) {
2008-03-28 23:31:21 +01:00
entries . push_back ( * it ) ;
2007-11-08 22:22:52 +01:00
}
}
sort ( entries . begin ( ) , entries . end ( ) ) ;
}
2006-03-19 22:37:30 +01:00
// remove oldest backup dirs if exceeding limit
void expire ( ) {
if ( m_logdir . size ( ) & & m_maxlogdirs > 0 ) {
vector < string > entries ;
2007-11-08 22:22:52 +01:00
getLogdirs ( m_logdir , entries ) ;
2006-03-19 22:37:30 +01:00
2008-03-24 22:42:47 +01:00
int deleted = 0 ;
2006-03-19 22:37:30 +01:00
for ( vector < string > : : iterator it = entries . begin ( ) ;
2008-03-24 22:42:47 +01:00
it ! = entries . end ( ) & & ( int ) entries . size ( ) - deleted > m_maxlogdirs ;
2006-03-19 22:37:30 +01:00
+ + it , + + deleted ) {
string path = m_logdir + " / " + * it ;
string msg = " removing " + path ;
LOG . info ( msg . c_str ( ) ) ;
2008-03-28 23:31:21 +01:00
rm_r ( path ) ;
2006-03-19 22:37:30 +01:00
}
}
}
// remove redirection of stderr and (optionally) also of logging
void restore ( bool all ) {
2007-03-31 18:22:40 +02:00
if ( ! m_restoreLog ) {
return ;
}
2006-03-19 22:37:30 +01:00
if ( all ) {
2007-04-21 14:33:23 +02:00
if ( m_logfile . size ( ) ) {
2008-01-12 23:53:50 +01:00
# ifdef POSIX_LOG
POSIX_LOG .
# endif
setLogFile ( NULL , " - " , false ) ;
2007-04-21 14:33:23 +02:00
}
2006-04-06 19:02:43 +02:00
LOG . setLevel ( m_oldLogLevel ) ;
2006-03-19 22:37:30 +01:00
} else {
2007-04-21 14:33:23 +02:00
if ( m_logfile . size ( ) ) {
2008-01-12 23:53:50 +01:00
# ifdef POSIX_LOG
POSIX_LOG .
# endif
setLogFile ( NULL , m_logfile . c_str ( ) , false ) ;
2007-04-21 14:33:23 +02:00
}
2006-03-19 22:37:30 +01:00
}
}
~ LogDir ( ) {
restore ( true ) ;
}
} ;
// this class owns the sync sources and (together with
// a logdir) handles writing of per-sync files as well
// as the final report (
2008-03-06 23:23:13 +01:00
class SourceList : public vector < EvolutionSyncSource * > {
2006-03-19 22:37:30 +01:00
LogDir m_logdir ; /**< our logging directory */
2007-04-21 14:33:23 +02:00
bool m_prepared ; /**< remember whether syncPrepare() dumped databases successfully */
2006-03-19 22:37:30 +01:00
bool m_doLogging ; /**< true iff additional files are to be written during sync */
2007-10-14 18:25:39 +02:00
SyncClient & m_client ; /**< client which holds the sync report after a sync */
2006-03-19 22:37:30 +01:00
bool m_reportTodo ; /**< true if syncDone() shall print a final report */
2008-03-18 14:36:59 +01:00
boost : : scoped_array < SyncSource * > m_sourceArray ; /** owns the array that is expected by SyncClient::sync() */
2007-11-08 22:22:52 +01:00
const bool m_quiet ; /**< avoid redundant printing to screen */
string m_previousLogdir ; /**< remember previous log dir before creating the new one */
2006-03-19 22:37:30 +01:00
2007-11-08 22:22:52 +01:00
/** create name in current (if set) or previous logdir */
string databaseName ( EvolutionSyncSource & source , const string suffix , string logdir = " " ) {
if ( ! logdir . size ( ) ) {
logdir = m_logdir . getLogdir ( ) ;
}
return logdir + " / " +
2006-03-19 22:37:30 +01:00
source . getName ( ) + " . " + suffix + " . " +
source . fileSuffix ( ) ;
}
2007-11-08 22:22:52 +01:00
public :
2007-03-23 22:00:32 +01:00
/**
2007-11-08 22:22:52 +01:00
* dump into files with a certain suffix
2007-03-23 22:00:32 +01:00
*/
2007-11-08 22:22:52 +01:00
void dumpDatabases ( const string & suffix ) {
2006-03-19 22:37:30 +01:00
ofstream out ;
2007-10-07 21:06:01 +02:00
# ifndef IPHONE
// output stream on iPhone raises exception even though it is in a good state;
// perhaps the missing C++ exception support is the reason:
// http://code.google.com/p/iphone-dev/issues/detail?id=48
2006-03-19 22:37:30 +01:00
out . exceptions ( ios_base : : badbit | ios_base : : failbit | ios_base : : eofbit ) ;
2007-10-07 21:06:01 +02:00
# endif
2006-03-19 22:37:30 +01:00
for ( iterator it = begin ( ) ;
it ! = end ( ) ;
+ + it ) {
2007-11-08 22:22:52 +01:00
string file = databaseName ( * * it , suffix ) ;
2007-10-07 21:06:01 +02:00
LOG . debug ( " creating %s " , file . c_str ( ) ) ;
2006-03-19 22:37:30 +01:00
out . open ( file . c_str ( ) ) ;
( * it ) - > exportData ( out ) ;
out . close ( ) ;
2007-10-07 21:06:01 +02:00
LOG . debug ( " %s created " , file . c_str ( ) ) ;
2006-03-19 22:37:30 +01:00
}
}
2007-11-08 22:22:52 +01:00
/** remove database dumps with a specific suffix */
void removeDatabases ( const string & removeSuffix ) {
for ( iterator it = begin ( ) ;
it ! = end ( ) ;
+ + it ) {
string file ;
file = databaseName ( * * it , removeSuffix ) ;
unlink ( file . c_str ( ) ) ;
}
}
2006-03-19 22:37:30 +01:00
2007-11-08 22:22:52 +01:00
SourceList ( const string & server , bool doLogging , SyncClient & client , bool quiet ) :
2006-03-19 22:37:30 +01:00
m_logdir ( server ) ,
2007-03-23 22:00:32 +01:00
m_prepared ( false ) ,
2006-03-19 22:37:30 +01:00
m_doLogging ( doLogging ) ,
2007-10-14 18:25:39 +02:00
m_client ( client ) ,
2007-11-08 22:22:52 +01:00
m_reportTodo ( true ) ,
m_quiet ( quiet )
{
2006-03-19 22:37:30 +01:00
}
// call as soon as logdir settings are known
2007-04-21 14:33:23 +02:00
void setLogdir ( const char * logDirPath , int maxlogdirs , int logLevel ) {
2007-11-08 22:22:52 +01:00
m_previousLogdir = m_logdir . previousLogdir ( logDirPath ) ;
2006-03-19 22:37:30 +01:00
if ( m_doLogging ) {
2007-04-21 14:33:23 +02:00
m_logdir . setLogdir ( logDirPath , maxlogdirs , logLevel ) ;
2006-04-06 19:02:43 +02:00
} else {
// at least increase log level
LOG . setLevel ( LOG_LEVEL_DEBUG ) ;
}
2006-03-19 22:37:30 +01:00
}
2007-11-08 22:22:52 +01:00
/** return previous log dir found in setLogdir() */
const string & getPrevLogdir ( ) const { return m_previousLogdir ; }
/** set directory for database files without actually redirecting the logging */
void setPath ( const string & path ) { m_logdir . setPath ( path ) ; }
/**
* If possible ( m_previousLogdir found ) and enabled ( ! m_quiet ) ,
* then dump changes applied locally .
*
* @ param oldSuffix suffix of old database dump : usually " after "
* @ param currentSuffix the current database dump suffix : " current "
* when not doing a sync , otherwise " before "
*/
bool dumpLocalChanges ( const string & oldSuffix , const string & newSuffix ) {
if ( m_quiet | | ! m_previousLogdir . size ( ) ) {
return false ;
}
cout < < " Local changes to be applied to server during synchronization: \n " ;
for ( iterator it = begin ( ) ;
it ! = end ( ) ;
+ + it ) {
string oldFile = databaseName ( * * it , oldSuffix , m_previousLogdir ) ;
string newFile = databaseName ( * * it , newSuffix ) ;
cout < < " *** " < < ( * it ) - > getName ( ) < < " *** \n " < < flush ;
string cmd = string ( " env CLIENT_TEST_COMPARISON_FAILED=10 CLIENT_TEST_LEFT_NAME='after last sync' CLIENT_TEST_RIGHT_NAME='current data' CLIENT_TEST_REMOVED='removed since last sync' CLIENT_TEST_ADDED='added since last sync' synccompare 2>/dev/null ' " ) +
oldFile + " ' ' " + newFile + " ' " ;
int ret = system ( cmd . c_str ( ) ) ;
switch ( ret = = - 1 ? ret : WEXITSTATUS ( ret ) ) {
case 0 :
cout < < " no changes \n " ;
break ;
case 10 :
break ;
default :
cout < < " Comparison was impossible. \n " ;
break ;
}
}
cout < < " \n " ;
return true ;
}
2006-03-19 22:37:30 +01:00
// call when all sync sources are ready to dump
// pre-sync databases
void syncPrepare ( ) {
2007-04-21 14:33:23 +02:00
if ( m_logdir . getLogfile ( ) . size ( ) & &
m_doLogging ) {
2006-03-19 22:37:30 +01:00
// dump initial databases
2007-11-08 22:22:52 +01:00
dumpDatabases ( " before " ) ;
// compare against the old "after" database dump
dumpLocalChanges ( " after " , " before " ) ;
// now remove the old database dump
removeDatabases ( " after " ) ;
2007-04-21 14:33:23 +02:00
m_prepared = true ;
2006-03-19 22:37:30 +01:00
}
}
// call at the end of a sync with success == true
// if all went well to print report
void syncDone ( bool success ) {
if ( m_doLogging ) {
// ensure that stderr is seen again
m_logdir . restore ( false ) ;
if ( m_reportTodo ) {
// haven't looked at result of sync yet;
// don't do it again
m_reportTodo = false ;
2007-03-23 22:00:32 +01:00
// dump datatbase after sync, but not if already dumping it at the beginning didn't complete
if ( m_prepared ) {
2007-10-14 20:19:44 +02:00
try {
dumpDatabases ( " after " ) ;
} catch ( const std : : exception & ex ) {
LOG . error ( " %s " , ex . what ( ) ) ;
m_prepared = false ;
}
2007-03-23 22:00:32 +01:00
}
2006-03-19 22:37:30 +01:00
2007-04-21 14:33:23 +02:00
string logfile = m_logdir . getLogfile ( ) ;
2008-01-12 23:53:50 +01:00
# ifndef LOG_HAVE_SET_LOGGER
// scan for error messages
2007-11-08 22:22:52 +01:00
if ( ! m_quiet & & logfile . size ( ) ) {
2007-04-21 14:33:23 +02:00
ifstream in ;
in . open ( m_logdir . getLogfile ( ) . c_str ( ) ) ;
while ( in . good ( ) ) {
string line ;
getline ( in , line ) ;
if ( line . find ( " [ERROR] " ) ! = line . npos ) {
success = false ;
2008-01-12 23:53:50 +01:00
cout < < line < < " \n " ;
2007-04-21 14:33:23 +02:00
} else if ( line . find ( " [INFO] " ) ! = line . npos ) {
cout < < line < < " \n " ;
}
2006-03-19 22:37:30 +01:00
}
2007-04-21 14:33:23 +02:00
in . close ( ) ;
2006-03-19 22:37:30 +01:00
}
2008-01-12 23:53:50 +01:00
# endif
2006-03-19 22:37:30 +01:00
2007-04-21 14:33:23 +02:00
cout < < flush ;
cerr < < flush ;
2006-03-19 22:37:30 +01:00
cout < < " \n " ;
if ( success ) {
cout < < " Synchronization successful. \n " ;
2007-04-21 14:33:23 +02:00
} else if ( logfile . size ( ) ) {
2006-03-19 22:37:30 +01:00
cout < < " Synchronization failed, see "
2007-04-21 14:33:23 +02:00
< < logfile
2006-03-19 22:37:30 +01:00
< < " for details. \n " ;
2007-04-21 14:33:23 +02:00
} else {
cout < < " Synchronization failed. \n " ;
2006-03-19 22:37:30 +01:00
}
2007-10-14 18:25:39 +02:00
// pretty-print report
2007-11-08 22:22:52 +01:00
if ( ! m_quiet ) {
cout < < " \n Changes applied during synchronization: \n " ;
}
2007-10-14 18:25:39 +02:00
SyncReport * report = m_client . getSyncReport ( ) ;
2007-11-08 22:22:52 +01:00
if ( ! m_quiet & & report ) {
2007-10-14 18:25:39 +02:00
cout < < " +-------------------|-------ON CLIENT-------|-------ON SERVER-------| \n " ;
cout < < " | | successful / total | successful / total | \n " ;
cout < < " | Source | NEW | MOD | DEL | NEW | MOD | DEL | \n " ;
const char * sep =
" +-------------------+-------+-------+-------+-------+-------+-------+ \n " ;
cout < < sep ;
for ( unsigned int i = 0 ; report - > getSyncSourceReport ( i ) ; i + + ) {
SyncSourceReport * ssr = report - > getSyncSourceReport ( i ) ;
if ( ssr - > getState ( ) = = SOURCE_INACTIVE ) {
continue ;
}
cout < < " | " < < right < < setw ( 18 ) < < ssr - > getSourceName ( ) < < " | " ;
static const char * const targets [ ] =
{ CLIENT , SERVER , NULL } ;
for ( int target = 0 ;
targets [ target ] ;
target + + ) {
static const char * const commands [ ] =
{ COMMAND_ADD , COMMAND_REPLACE , COMMAND_DELETE , NULL } ;
for ( int command = 0 ;
commands [ command ] ;
command + + ) {
cout < < right < < setw ( 3 ) < <
ssr - > getItemReportSuccessfulCount ( targets [ target ] , commands [ command ] ) ;
cout < < " / " ;
cout < < left < < setw ( 3 ) < <
ssr - > getItemReportCount ( targets [ target ] , commands [ command ] ) ;
cout < < " | " ;
}
}
cout < < " \n " ;
}
cout < < sep ;
}
2007-03-23 22:00:32 +01:00
// compare databases?
2007-11-08 22:22:52 +01:00
if ( ! m_quiet & & m_prepared ) {
2007-11-10 14:26:11 +01:00
cout < < " \n Changes applied to client during synchronization: \n " ;
2007-03-23 22:00:32 +01:00
for ( iterator it = begin ( ) ;
it ! = end ( ) ;
+ + it ) {
cout < < " *** " < < ( * it ) - > getName ( ) < < " *** \n " < < flush ;
string before = databaseName ( * * it , " before " ) ;
string after = databaseName ( * * it , " after " ) ;
string cmd = string ( " synccompare ' " ) +
before + " ' ' " + after +
" ' && echo 'no changes' " ;
system ( cmd . c_str ( ) ) ;
}
cout < < " \n " ;
2006-03-19 22:37:30 +01:00
}
if ( success ) {
m_logdir . expire ( ) ;
}
2005-11-26 22:16:03 +01:00
}
}
2006-03-19 22:37:30 +01:00
}
2006-09-07 21:47:29 +02:00
/** returns current sources as array as expected by SyncClient::sync(), memory owned by this class */
SyncSource * * getSourceArray ( ) {
2008-03-18 14:36:59 +01:00
m_sourceArray . reset ( new SyncSource * [ size ( ) + 1 ] ) ;
2006-09-07 21:47:29 +02:00
int index = 0 ;
for ( iterator it = begin ( ) ;
it ! = end ( ) ;
+ + it ) {
2008-03-18 14:36:59 +01:00
m_sourceArray [ index ] = * it ;
2006-09-07 21:47:29 +02:00
index + + ;
}
2008-03-18 14:36:59 +01:00
m_sourceArray [ index ] = 0 ;
return & m_sourceArray [ 0 ] ;
2006-09-07 21:47:29 +02:00
}
2006-03-19 22:37:30 +01:00
~ SourceList ( ) {
// free sync sources
for ( iterator it = begin ( ) ;
it ! = end ( ) ;
+ + it ) {
delete * it ;
}
}
2008-03-06 23:23:13 +01:00
/** find sync source by name */
EvolutionSyncSource * operator [ ] ( const string & name ) {
for ( iterator it = begin ( ) ;
it ! = end ( ) ;
+ + it ) {
if ( name = = ( * it ) - > getName ( ) ) {
return * it ;
}
}
return NULL ;
}
/** find by index */
EvolutionSyncSource * operator [ ] ( int index ) { return vector < EvolutionSyncSource * > : : operator [ ] ( index ) ; }
2006-03-19 22:37:30 +01:00
} ;
2006-08-14 22:52:34 +02:00
void unref ( SourceList * sourceList )
{
delete sourceList ;
}
2007-10-11 23:02:49 +02:00
void EvolutionSyncClient : : throwError ( const string & error )
{
# ifdef IPHONE
/*
* Catching the runtime_exception fails due to a toolchain problem ,
* so do the error handling now and abort : because there is just
* one sync source this is probably the only thing that can be done .
* Still , it ' s not nice on the server . . .
*/
2007-11-03 18:07:42 +01:00
fatalError ( NULL , error . c_str ( ) ) ;
# else
throw runtime_error ( error ) ;
# endif
}
void EvolutionSyncClient : : fatalError ( void * object , const char * error )
{
LOG . error ( " %s " , error ) ;
2007-10-11 23:02:49 +02:00
if ( m_sourceListPtr ) {
m_sourceListPtr - > syncDone ( false ) ;
}
exit ( 1 ) ;
2007-11-03 18:07:42 +01:00
}
2007-12-15 17:43:12 +01:00
/*
* There have been segfaults inside glib in the background
* thread which ran the second event loop . Disabled it again ,
* even though the synchronous EDS API calls will block then
* when EDS dies .
*/
#if 0 && defined(HAVE_GLIB) && defined(HAVE_EDS)
2007-11-03 18:07:42 +01:00
# define RUN_GLIB_LOOP
# endif
# ifdef RUN_GLIB_LOOP
# include <pthread.h>
2007-11-28 19:05:22 +01:00
# include <signal.h>
2007-11-03 18:07:42 +01:00
static void * mainLoopThread ( void * )
{
2007-11-28 19:05:22 +01:00
// The test framework uses SIGALRM for timeouts.
// Block the signal here because a) the signal handler
// prints a stack back trace when called and we are not
// interessted in the background thread's stack and b)
// it seems to have confused glib/libebook enough to
// access invalid memory and segfault when it gets the SIGALRM.
sigset_t blocked ;
sigemptyset ( & blocked ) ;
sigaddset ( & blocked , SIGALRM ) ;
pthread_sigmask ( SIG_BLOCK , & blocked , NULL ) ;
2007-11-03 18:07:42 +01:00
GMainLoop * mainloop = g_main_loop_new ( NULL , TRUE ) ;
if ( mainloop ) {
g_main_loop_run ( mainloop ) ;
g_main_loop_unref ( mainloop ) ;
}
return NULL ;
}
# endif
void EvolutionSyncClient : : startLoopThread ( )
{
# ifdef RUN_GLIB_LOOP
// when using Evolution we must have a running main loop,
// otherwise loss of connection won't be reported to us
static pthread_t loopthread ;
static bool loopthreadrunning ;
if ( ! loopthreadrunning ) {
loopthreadrunning = ! pthread_create ( & loopthread , NULL , mainLoopThread , NULL ) ;
}
2007-10-11 23:02:49 +02:00
# endif
}
2008-03-06 23:23:13 +01:00
AbstractSyncSourceConfig * EvolutionSyncClient : : getAbstractSyncSourceConfig ( const char * name ) const
{
return m_sourceListPtr ? ( * m_sourceListPtr ) [ name ] : NULL ;
}
AbstractSyncSourceConfig * EvolutionSyncClient : : getAbstractSyncSourceConfig ( unsigned int i ) const
{
return m_sourceListPtr ? ( * m_sourceListPtr ) [ i ] : NULL ;
}
unsigned int EvolutionSyncClient : : getAbstractSyncSourceConfigsCount ( ) const
{
return m_sourceListPtr ? m_sourceListPtr - > size ( ) : 0 ;
}
void EvolutionSyncClient : : initSources ( SourceList & sourceList )
2007-11-08 22:22:52 +01:00
{
2007-11-22 22:06:21 +01:00
set < string > unmatchedSources = m_sources ;
2008-03-06 23:23:13 +01:00
list < string > configuredSources = getSyncSources ( ) ;
for ( list < string > : : const_iterator it = configuredSources . begin ( ) ;
it ! = configuredSources . end ( ) ;
it + + ) {
const string & name ( * it ) ;
boost : : shared_ptr < PersistentEvolutionSyncSourceConfig > sc ( getSyncSourceConfig ( name ) ) ;
2007-11-08 22:22:52 +01:00
// is the source enabled?
2008-03-06 23:23:13 +01:00
string sync = sc - > getSync ( ) ;
2008-03-20 22:58:26 +01:00
bool enabled = sync ! = " disabled " ;
2008-03-06 23:23:13 +01:00
bool overrideMode = false ;
2007-11-08 22:22:52 +01:00
// override state?
if ( m_sources . size ( ) ) {
2008-03-06 23:23:13 +01:00
if ( m_sources . find ( sc - > getName ( ) ) ! = m_sources . end ( ) ) {
2007-11-08 22:22:52 +01:00
if ( ! enabled ) {
2008-03-06 23:23:13 +01:00
overrideMode = true ;
2007-11-08 22:22:52 +01:00
enabled = true ;
}
2008-03-06 23:23:13 +01:00
unmatchedSources . erase ( sc - > getName ( ) ) ;
2007-11-08 22:22:52 +01:00
} else {
enabled = false ;
}
}
if ( enabled ) {
2008-03-06 23:23:13 +01:00
EvolutionSyncSourceParams params ( name ,
getSyncSourceNodes ( name ) ,
string ( " sync4jevolution: " ) + getSyncURL ( ) + " / " + name ) ;
2008-03-09 18:11:44 +01:00
// the sync mode has to be set before instantiating the source
// because the client library reads the preferredSyncMode at that time:
// have to take a shortcut and set the property via its name
2008-03-06 23:23:13 +01:00
if ( overrideMode ) {
params . m_nodes . m_configNode - > addFilter ( " sync " , " two-way " ) ;
2007-11-08 22:22:52 +01:00
}
EvolutionSyncSource * syncSource =
2008-03-06 23:23:13 +01:00
EvolutionSyncSource : : createSource ( params ) ;
2007-11-08 22:22:52 +01:00
if ( ! syncSource ) {
2008-03-06 23:23:13 +01:00
throwError ( name + " : type unknown " ) ;
2007-11-08 22:22:52 +01:00
}
sourceList . push_back ( syncSource ) ;
// also open it; failing now is still safe
syncSource - > open ( ) ;
}
}
2007-11-22 22:06:21 +01:00
// check whether there were any sources specified which do not exist
if ( unmatchedSources . size ( ) ) {
string sources ;
for ( set < string > : : const_iterator it = unmatchedSources . begin ( ) ;
it ! = unmatchedSources . end ( ) ;
it + + ) {
if ( sources . size ( ) ) {
sources + = " " ;
}
sources + = * it ;
}
throwError ( string ( " no such source(s): " ) + sources ) ;
}
2007-11-08 22:22:52 +01:00
}
2006-08-06 09:56:41 +02:00
int EvolutionSyncClient : : sync ( )
2006-03-19 22:37:30 +01:00
{
2007-03-31 18:22:40 +02:00
int res = 1 ;
2006-09-07 21:47:29 +02:00
2008-03-06 23:23:13 +01:00
if ( ! exists ( ) ) {
LOG . error ( " No configuration for server \" %s \" found. " , m_server . c_str ( ) ) ;
2007-10-11 23:02:49 +02:00
throwError ( " cannot proceed without configuration " ) ;
2006-08-06 09:56:41 +02:00
}
2006-09-07 21:47:29 +02:00
// redirect logging as soon as possible
2007-11-08 22:22:52 +01:00
SourceList sourceList ( m_server , m_doLogging , * this , m_quiet ) ;
2007-10-11 23:02:49 +02:00
m_sourceListPtr = & sourceList ;
2006-11-27 22:08:12 +01:00
2007-03-23 22:00:32 +01:00
try {
2008-03-06 23:23:13 +01:00
sourceList . setLogdir ( getLogDir ( ) ,
getMaxLogDirs ( ) ,
getLogLevel ( ) ) ;
2006-08-06 09:56:41 +02:00
2007-11-04 18:33:27 +01:00
// dump some summary information at the beginning of the log
2008-01-12 23:53:50 +01:00
# ifdef LOG_HAVE_DEVELOPER
# define LOG_DEVELOPER developer
# else
# define LOG_DEVELOPER debug
# endif
2008-03-06 23:23:13 +01:00
LOG . LOG_DEVELOPER ( " SyncML server account: %s " , getUsername ( ) ) ;
LOG . LOG_DEVELOPER ( " client: SyncEvolution %s for %s " ,
getSwv ( ) , getDevType ( ) ) ;
2007-11-04 18:33:27 +01:00
time_t now = time ( NULL ) ;
2008-01-12 23:53:50 +01:00
LOG . LOG_DEVELOPER ( " current UTC date and time: %s " , asctime ( gmtime ( & now ) ) ) ;
2007-11-04 18:33:27 +01:00
2008-03-06 23:23:13 +01:00
initSources ( sourceList ) ;
2006-09-07 21:47:29 +02:00
2007-03-23 22:00:32 +01:00
// give derived class also a chance to update the configs
2008-03-06 23:23:13 +01:00
prepare ( sourceList . getSourceArray ( ) ) ;
2006-09-29 01:02:43 +02:00
2007-11-03 18:07:42 +01:00
// start background thread if not running yet:
// necessary to catch problems with Evolution backend
startLoopThread ( ) ;
2007-03-23 22:00:32 +01:00
// ready to go: dump initial databases and prepare for final report
sourceList . syncPrepare ( ) ;
2006-09-07 21:47:29 +02:00
2007-03-23 22:00:32 +01:00
// do it
2008-03-06 23:23:13 +01:00
res = SyncClient : : sync ( * this , sourceList . getSourceArray ( ) ) ;
2006-09-07 21:47:29 +02:00
2007-04-21 15:14:43 +02:00
// store modified properties: must be done even after failed
// sync because the source's anchor might have been reset
2008-03-06 23:23:13 +01:00
flush ( ) ;
2007-04-21 15:14:43 +02:00
2007-03-23 22:00:32 +01:00
if ( res ) {
2008-02-14 20:48:51 +01:00
if ( getLastErrorCode ( ) & & getLastErrorMsg ( ) & & getLastErrorMsg ( ) [ 0 ] ) {
throwError ( getLastErrorMsg ( ) ) ;
2007-03-23 22:00:32 +01:00
}
// no error code/description?!
2007-10-11 23:02:49 +02:00
throwError ( " sync failed without an error description, check log " ) ;
2006-09-07 21:47:29 +02:00
}
2006-03-19 22:37:30 +01:00
2007-03-23 22:00:32 +01:00
// all went well: print final report before cleaning up
sourceList . syncDone ( true ) ;
2007-03-31 18:22:40 +02:00
res = 0 ;
2007-03-23 22:00:32 +01:00
} catch ( const std : : exception & ex ) {
LOG . error ( " %s " , ex . what ( ) ) ;
// something went wrong, but try to write .after state anyway
2007-10-11 23:02:49 +02:00
m_sourceListPtr = NULL ;
2007-03-23 22:00:32 +01:00
sourceList . syncDone ( false ) ;
} catch ( . . . ) {
LOG . error ( " unknown error " ) ;
2007-10-11 23:02:49 +02:00
m_sourceListPtr = NULL ;
2007-03-23 22:00:32 +01:00
sourceList . syncDone ( false ) ;
}
2006-12-17 17:33:45 +01:00
2007-10-11 23:02:49 +02:00
m_sourceListPtr = NULL ;
2007-03-31 18:22:40 +02:00
return res ;
2005-11-26 22:16:03 +01:00
}
2007-11-08 22:22:52 +01:00
2008-03-06 23:23:13 +01:00
void EvolutionSyncClient : : prepare ( SyncSource * * sources ) {
2007-11-08 22:22:52 +01:00
if ( m_syncMode ! = SYNC_NONE ) {
for ( SyncSource * * source = sources ;
* source ;
source + + ) {
( * source ) - > setPreferredSyncMode ( m_syncMode ) ;
}
}
}
void EvolutionSyncClient : : status ( )
{
2008-03-06 23:23:13 +01:00
EvolutionSyncConfig config ( m_server ) ;
if ( ! exists ( ) ) {
LOG . error ( " No configuration for server \" %s \" found. " , m_server . c_str ( ) ) ;
2007-11-08 22:22:52 +01:00
throwError ( " cannot proceed without configuration " ) ;
}
SourceList sourceList ( m_server , false , * this , false ) ;
2008-03-06 23:23:13 +01:00
initSources ( sourceList ) ;
2007-11-08 22:22:52 +01:00
2008-03-06 23:23:13 +01:00
sourceList . setLogdir ( getLogDir ( ) , 0 , LOG_LEVEL_NONE ) ;
2007-11-08 22:22:52 +01:00
LOG . setLevel ( LOG_LEVEL_INFO ) ;
string prevLogdir = sourceList . getPrevLogdir ( ) ;
bool found = access ( prevLogdir . c_str ( ) , R_OK | X_OK ) = = 0 ;
if ( found ) {
try {
sourceList . setPath ( prevLogdir ) ;
sourceList . dumpDatabases ( " current " ) ;
sourceList . dumpLocalChanges ( " after " , " current " ) ;
} catch ( const std : : exception & ex ) {
LOG . error ( " %s " , ex . what ( ) ) ;
}
} else {
cerr < < " Previous log directory not found. \n " ;
2008-03-06 23:23:13 +01:00
if ( ! getLogDir ( ) | | ! getLogDir ( ) [ 0 ] ) {
2007-11-08 22:22:52 +01:00
cerr < < " Enable the 'logdir' option and synchronize to use this feature. \n " ;
}
}
}