00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "EvolutionSyncClient.h"
00020 #include "EvolutionSyncSource.h"
00021 #include "SyncEvolutionUtil.h"
00022
00023 #include <posix/base/posixlog.h>
00024
00025 #include <list>
00026 #include <memory>
00027 #include <vector>
00028 #include <sstream>
00029 #include <fstream>
00030 #include <iomanip>
00031 #include <iostream>
00032 #include <stdexcept>
00033 using namespace std;
00034
00035 #include <boost/algorithm/string/predicate.hpp>
00036 #include <boost/algorithm/string/replace.hpp>
00037 #include <boost/foreach.hpp>
00038
00039 #include <sys/stat.h>
00040 #include <pwd.h>
00041 #include <unistd.h>
00042 #include <dirent.h>
00043 #include <errno.h>
00044
00045 SourceList *EvolutionSyncClient::m_sourceListPtr;
00046
00047 EvolutionSyncClient::EvolutionSyncClient(const string &server,
00048 bool doLogging,
00049 const set<string> &sources) :
00050 EvolutionSyncConfig(server),
00051 m_server(server),
00052 m_sources(sources),
00053 m_doLogging(doLogging),
00054 m_syncMode(SYNC_NONE),
00055 m_quiet(false)
00056 {
00057 }
00058
00059 EvolutionSyncClient::~EvolutionSyncClient()
00060 {
00061 }
00062
00063
00064
00065
00066
00067 class LogDir {
00068 string m_logdir;
00069 int m_maxlogdirs;
00070 string m_prefix;
00071 string m_path;
00072 string m_logfile;
00073 const string &m_server;
00074 LogLevel m_oldLogLevel;
00075 bool m_restoreLog;
00076
00077 public:
00078 LogDir(const string &server) : m_server(server),
00079 m_restoreLog(false)
00080 {
00081
00082 m_prefix = "SyncEvolution-";
00083 m_prefix += m_server;
00084
00085
00086 stringstream path;
00087 char *tmp = getenv("TMPDIR");
00088 if (tmp) {
00089 path << tmp;
00090 } else {
00091 path << "/tmp";
00092 }
00093 path << "/SyncEvolution-";
00094 struct passwd *user = getpwuid(getuid());
00095 if (user && user->pw_name) {
00096 path << user->pw_name;
00097 } else {
00098 path << getuid();
00099 }
00100 path << "-" << m_server;
00101
00102 m_path = path.str();
00103 }
00104
00105
00106
00107
00108
00109
00110
00111 string previousLogdir(const char *path) {
00112 string logdir;
00113
00114 if (path && !strcasecmp(path, "none")) {
00115 return "";
00116 } else if (path && path[0]) {
00117 vector<string> entries;
00118 try {
00119 getLogdirs(path, entries);
00120 } catch(const std::exception &ex) {
00121 LOG.error("%s", ex.what());
00122 return "";
00123 }
00124
00125 logdir = entries.size() ? string(path) + "/" + entries[entries.size()-1] : "";
00126 } else {
00127 logdir = m_path;
00128 }
00129
00130 if (access(logdir.c_str(), R_OK|X_OK) == 0) {
00131 return logdir;
00132 } else {
00133 return "";
00134 }
00135 }
00136
00137
00138
00139
00140
00141 void setLogdir(const char *path, int maxlogdirs, int logLevel = 0) {
00142 m_maxlogdirs = maxlogdirs;
00143 if (path && !strcasecmp(path, "none")) {
00144 m_logfile = "";
00145 } else if (path && path[0]) {
00146 m_logdir = path;
00147
00148
00149 time_t ts = time(NULL);
00150 struct tm *tm = localtime(&ts);
00151 stringstream base;
00152 base << path << "/"
00153 << m_prefix
00154 << "-"
00155 << setfill('0')
00156 << setw(4) << tm->tm_year + 1900 << "-"
00157 << setw(2) << tm->tm_mon + 1 << "-"
00158 << setw(2) << tm->tm_mday << "-"
00159 << setw(2) << tm->tm_hour << "-"
00160 << setw(2) << tm->tm_min;
00161 int seq = 0;
00162 while (true) {
00163 stringstream path;
00164 path << base.str();
00165 if (seq) {
00166 path << "-" << seq;
00167 }
00168 m_path = path.str();
00169 if (!mkdir(m_path.c_str(), S_IRWXU)) {
00170 break;
00171 }
00172 if (errno != EEXIST) {
00173 LOG.debug("%s: %s", m_path.c_str(), strerror(errno));
00174 EvolutionSyncClient::throwError(m_path, errno);
00175 }
00176 seq++;
00177 }
00178 m_logfile = m_path + "/client.log";
00179 } else {
00180
00181 if (mkdir(m_path.c_str(), S_IRWXU)) {
00182 if (errno != EEXIST) {
00183 EvolutionSyncClient::throwError(m_path, errno);
00184 }
00185 }
00186 m_logfile = m_path + "/client.log";
00187 }
00188
00189 if (m_logfile.size()) {
00190
00191
00192 FILE *file = fopen(m_logfile.c_str(), "w");
00193 if (file) {
00194 fclose(file);
00195 #ifdef POSIX_LOG
00196 POSIX_LOG.
00197 #endif
00198 setLogFile(NULL, m_logfile.c_str(), true);
00199 } else {
00200 LOG.error("creating log file %s failed", m_logfile.c_str());
00201 }
00202 }
00203 m_oldLogLevel = LOG.getLevel();
00204 LOG.setLevel(logLevel > 0 ? (LogLevel)(logLevel - 1) :
00205 m_logfile.size() ? LOG_LEVEL_DEBUG :
00206 LOG_LEVEL_INFO );
00207 m_restoreLog = true;
00208 }
00209
00210
00211 void setPath(const string &path) { m_path = path; }
00212
00213
00214 const string &getLogdir() {
00215 return m_path;
00216 }
00217
00218
00219 const string &getLogfile() {
00220 return m_logfile;
00221 }
00222
00223
00224 void getLogdirs(const string &logdir, vector<string> &entries) {
00225 ReadDir dir(logdir);
00226 BOOST_FOREACH(const string &entry, dir) {
00227 if (boost::starts_with(entry, m_prefix)) {
00228 entries.push_back(entry);
00229 }
00230 }
00231 sort(entries.begin(), entries.end());
00232 }
00233
00234
00235
00236 void expire() {
00237 if (m_logdir.size() && m_maxlogdirs > 0 ) {
00238 vector<string> entries;
00239 getLogdirs(m_logdir, entries);
00240
00241 int deleted = 0;
00242 for (vector<string>::iterator it = entries.begin();
00243 it != entries.end() && (int)entries.size() - deleted > m_maxlogdirs;
00244 ++it, ++deleted) {
00245 string path = m_logdir + "/" + *it;
00246 string msg = "removing " + path;
00247 LOG.info(msg.c_str());
00248 rm_r(path);
00249 }
00250 }
00251 }
00252
00253
00254 void restore(bool all) {
00255 if (!m_restoreLog) {
00256 return;
00257 }
00258
00259 if (all) {
00260 if (m_logfile.size()) {
00261 #ifdef POSIX_LOG
00262 POSIX_LOG.
00263 #endif
00264 setLogFile(NULL, "-", false);
00265 }
00266 LOG.setLevel(m_oldLogLevel);
00267 } else {
00268 if (m_logfile.size()) {
00269 #ifdef POSIX_LOG
00270 POSIX_LOG.
00271 #endif
00272 setLogFile(NULL, m_logfile.c_str(), false);
00273 }
00274 }
00275 }
00276
00277 ~LogDir() {
00278 restore(true);
00279 }
00280 };
00281
00282
00283
00284
00285 class SourceList : public vector<EvolutionSyncSource *> {
00286 LogDir m_logdir;
00287 bool m_prepared;
00288 bool m_doLogging;
00289 SyncClient &m_client;
00290 bool m_reportTodo;
00291 boost::scoped_array<SyncSource *> m_sourceArray;
00292 const bool m_quiet;
00293 string m_previousLogdir;
00294
00295
00296 string databaseName(EvolutionSyncSource &source, const string suffix, string logdir = "") {
00297 if (!logdir.size()) {
00298 logdir = m_logdir.getLogdir();
00299 }
00300 return logdir + "/" +
00301 source.getName() + "." + suffix + "." +
00302 source.fileSuffix();
00303 }
00304
00305 public:
00306
00307
00308
00309 void dumpDatabases(const string &suffix) {
00310 ofstream out;
00311 #ifndef IPHONE
00312
00313
00314
00315 out.exceptions(ios_base::badbit|ios_base::failbit|ios_base::eofbit);
00316 #endif
00317
00318 BOOST_FOREACH(EvolutionSyncSource *source, *this) {
00319 string file = databaseName(*source, suffix);
00320 LOG.debug("creating %s", file.c_str());
00321 out.open(file.c_str());
00322 source->exportData(out);
00323 out.close();
00324 LOG.debug("%s created", file.c_str());
00325 }
00326 }
00327
00328
00329 void removeDatabases(const string &removeSuffix) {
00330 BOOST_FOREACH(EvolutionSyncSource *source, *this) {
00331 string file;
00332
00333 file = databaseName(*source, removeSuffix);
00334 unlink(file.c_str());
00335 }
00336 }
00337
00338 SourceList(const string &server, bool doLogging, SyncClient &client, bool quiet) :
00339 m_logdir(server),
00340 m_prepared(false),
00341 m_doLogging(doLogging),
00342 m_client(client),
00343 m_reportTodo(true),
00344 m_quiet(quiet)
00345 {
00346 }
00347
00348
00349 void setLogdir(const char *logDirPath, int maxlogdirs, int logLevel) {
00350 m_previousLogdir = m_logdir.previousLogdir(logDirPath);
00351 if (m_doLogging) {
00352 m_logdir.setLogdir(logDirPath, maxlogdirs, logLevel);
00353 } else {
00354
00355 LOG.setLevel(LOG_LEVEL_DEBUG);
00356 }
00357 }
00358
00359
00360 const string &getPrevLogdir() const { return m_previousLogdir; }
00361
00362
00363 void setPath(const string &path) { m_logdir.setPath(path); }
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373 bool dumpLocalChanges(const string &oldSuffix, const string &newSuffix) {
00374 if (m_quiet || !m_previousLogdir.size()) {
00375 return false;
00376 }
00377
00378 cout << "Local changes to be applied to server during synchronization:\n";
00379 BOOST_FOREACH(EvolutionSyncSource *source, *this) {
00380 string oldFile = databaseName(*source, oldSuffix, m_previousLogdir);
00381 string newFile = databaseName(*source, newSuffix);
00382 cout << "*** " << source->getName() << " ***\n" << flush;
00383 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 '" ) +
00384 oldFile + "' '" + newFile + "'";
00385 int ret = system(cmd.c_str());
00386 switch (ret == -1 ? ret : WEXITSTATUS(ret)) {
00387 case 0:
00388 cout << "no changes\n";
00389 break;
00390 case 10:
00391 break;
00392 default:
00393 cout << "Comparison was impossible.\n";
00394 break;
00395 }
00396 }
00397 cout << "\n";
00398 return true;
00399 }
00400
00401
00402
00403 void syncPrepare() {
00404 if (m_logdir.getLogfile().size() &&
00405 m_doLogging) {
00406
00407 dumpDatabases("before");
00408
00409 dumpLocalChanges("after", "before");
00410
00411 removeDatabases("after");
00412 m_prepared = true;
00413 }
00414 }
00415
00416
00417
00418 void syncDone(bool success) {
00419 if (m_doLogging) {
00420
00421 m_logdir.restore(false);
00422
00423 if (m_reportTodo) {
00424
00425
00426 m_reportTodo = false;
00427
00428
00429 if (m_prepared) {
00430 try {
00431 dumpDatabases("after");
00432 } catch (const std::exception &ex) {
00433 LOG.error( "%s", ex.what() );
00434 m_prepared = false;
00435 }
00436 }
00437
00438 string logfile = m_logdir.getLogfile();
00439 #ifndef LOG_HAVE_SET_LOGGER
00440
00441 if (!m_quiet && logfile.size()) {
00442 ifstream in;
00443 in.open(m_logdir.getLogfile().c_str());
00444 while (in.good()) {
00445 string line;
00446 getline(in, line);
00447 if (line.find("[ERROR]") != line.npos) {
00448 success = false;
00449 cout << line << "\n";
00450 } else if (line.find("[INFO]") != line.npos) {
00451 cout << line << "\n";
00452 }
00453 }
00454 in.close();
00455 }
00456 #endif
00457
00458 cout << flush;
00459 cerr << flush;
00460 cout << "\n";
00461 if (success) {
00462 cout << "Synchronization successful.\n";
00463 } else if (logfile.size()) {
00464 cout << "Synchronization failed, see "
00465 << logfile
00466 << " for details.\n";
00467 } else {
00468 cout << "Synchronization failed.\n";
00469 }
00470
00471
00472 if (!m_quiet) {
00473 cout << "\nChanges applied during synchronization:\n";
00474 }
00475 SyncReport *report = m_client.getSyncReport();
00476 if (!m_quiet && report) {
00477
00478 cout << "+-------------------|-------ON CLIENT-------|-------ON SERVER-------|\n";
00479 cout << "| | successful / total | successful / total |\n";
00480 cout << "| Source | NEW | MOD | DEL | NEW | MOD | DEL |\n";
00481 const char *sep =
00482 "+-------------------+-------+-------+-------+-------+-------+-------+\n";
00483 cout << sep;
00484
00485 for (unsigned int i = 0; report->getSyncSourceReport(i); i++) {
00486 SyncSourceReport* ssr = report->getSyncSourceReport(i);
00487
00488 if (ssr->getState() == SOURCE_INACTIVE) {
00489 continue;
00490 }
00491
00492 cout << "|" << right << setw(18) << ssr->getSourceName() << " |";
00493 static const char * const targets[] =
00494 { CLIENT, SERVER, NULL };
00495 for (int target = 0;
00496 targets[target];
00497 target++) {
00498 static const char * const commands[] =
00499 { COMMAND_ADD, COMMAND_REPLACE, COMMAND_DELETE, NULL };
00500 for (int command = 0;
00501 commands[command];
00502 command++) {
00503 cout << right << setw(3) <<
00504 ssr->getItemReportSuccessfulCount(targets[target], commands[command]);
00505 cout << "/";
00506 cout << left << setw(3) <<
00507 ssr->getItemReportCount(targets[target], commands[command]);
00508 cout << "|";
00509 }
00510 }
00511 cout << "\n";
00512 }
00513 cout << sep;
00514 }
00515
00516
00517 if (!m_quiet && m_prepared) {
00518 cout << "\nChanges applied to client during synchronization:\n";
00519 BOOST_FOREACH(EvolutionSyncSource *source, *this) {
00520 cout << "*** " << source->getName() << " ***\n" << flush;
00521
00522 string before = databaseName(*source, "before");
00523 string after = databaseName(*source, "after");
00524 string cmd = string("synccompare '" ) +
00525 before + "' '" + after +
00526 "' && echo 'no changes'";
00527 system(cmd.c_str());
00528 }
00529 cout << "\n";
00530 }
00531
00532 if (success) {
00533 m_logdir.expire();
00534 }
00535 }
00536 }
00537 }
00538
00539
00540 SyncSource **getSourceArray() {
00541 m_sourceArray.reset(new SyncSource *[size() + 1]);
00542
00543 int index = 0;
00544 BOOST_FOREACH(EvolutionSyncSource *source, *this) {
00545 m_sourceArray[index] = source;
00546 index++;
00547 }
00548 m_sourceArray[index] = 0;
00549 return &m_sourceArray[0];
00550 }
00551
00552
00553 set<string> getSources() {
00554 set<string> res;
00555
00556 BOOST_FOREACH(EvolutionSyncSource *source, *this) {
00557 res.insert(source->getName());
00558 }
00559 return res;
00560 }
00561
00562 ~SourceList() {
00563
00564 BOOST_FOREACH(EvolutionSyncSource *source, *this) {
00565 delete source;
00566 }
00567 }
00568
00569
00570 EvolutionSyncSource *operator [] (const string &name) {
00571 BOOST_FOREACH(EvolutionSyncSource *source, *this) {
00572 if (name == source->getName()) {
00573 return source;
00574 }
00575 }
00576 return NULL;
00577 }
00578
00579
00580 EvolutionSyncSource *operator [] (int index) { return vector<EvolutionSyncSource *>::operator [] (index); }
00581 };
00582
00583 void unref(SourceList *sourceList)
00584 {
00585 delete sourceList;
00586 }
00587
00588 string EvolutionSyncClient::askPassword(const string &descr)
00589 {
00590 char buffer[256];
00591
00592 printf("Enter password for %s: ",
00593 descr.c_str());
00594 fflush(stdout);
00595 if (fgets(buffer, sizeof(buffer), stdin) &&
00596 strcmp(buffer, "\n")) {
00597 size_t len = strlen(buffer);
00598 if (len && buffer[len - 1] == '\n') {
00599 buffer[len - 1] = 0;
00600 }
00601 return buffer;
00602 } else {
00603 throwError(string("could not read password for ") + descr);
00604 return "";
00605 }
00606 }
00607
00608 void EvolutionSyncClient::throwError(const string &error)
00609 {
00610 #ifdef IPHONE
00611
00612
00613
00614
00615
00616
00617 fatalError(NULL, error.c_str());
00618 #else
00619 throw runtime_error(error);
00620 #endif
00621 }
00622
00623 void EvolutionSyncClient::throwError(const string &action, int error)
00624 {
00625 throwError(action + ": " + strerror(error));
00626 }
00627
00628 void EvolutionSyncClient::fatalError(void *object, const char *error)
00629 {
00630 LOG.error("%s", error);
00631 if (m_sourceListPtr) {
00632 m_sourceListPtr->syncDone(false);
00633 }
00634 exit(1);
00635 }
00636
00637
00638
00639
00640
00641
00642
00643 #if 0 && defined(HAVE_GLIB) && defined(HAVE_EDS)
00644 # define RUN_GLIB_LOOP
00645 #endif
00646
00647 #ifdef RUN_GLIB_LOOP
00648 #include <pthread.h>
00649 #include <signal.h>
00650 static void *mainLoopThread(void *)
00651 {
00652
00653
00654
00655
00656
00657
00658 sigset_t blocked;
00659 sigemptyset(&blocked);
00660 sigaddset(&blocked, SIGALRM);
00661 pthread_sigmask(SIG_BLOCK, &blocked, NULL);
00662
00663 GMainLoop *mainloop = g_main_loop_new(NULL, TRUE);
00664 if (mainloop) {
00665 g_main_loop_run(mainloop);
00666 g_main_loop_unref(mainloop);
00667 }
00668 return NULL;
00669 }
00670 #endif
00671
00672 void EvolutionSyncClient::startLoopThread()
00673 {
00674 #ifdef RUN_GLIB_LOOP
00675
00676
00677 static pthread_t loopthread;
00678 static bool loopthreadrunning;
00679 if (!loopthreadrunning) {
00680 loopthreadrunning = !pthread_create(&loopthread, NULL, mainLoopThread, NULL);
00681 }
00682 #endif
00683 }
00684
00685 AbstractSyncSourceConfig* EvolutionSyncClient::getAbstractSyncSourceConfig(const char* name) const
00686 {
00687 return m_sourceListPtr ? (*m_sourceListPtr)[name] : NULL;
00688 }
00689
00690 AbstractSyncSourceConfig* EvolutionSyncClient::getAbstractSyncSourceConfig(unsigned int i) const
00691 {
00692 return m_sourceListPtr ? (*m_sourceListPtr)[i] : NULL;
00693 }
00694
00695 unsigned int EvolutionSyncClient::getAbstractSyncSourceConfigsCount() const
00696 {
00697 return m_sourceListPtr ? m_sourceListPtr->size() : 0;
00698 }
00699
00700 void EvolutionSyncClient::setConfigFilter(bool sync, const FilterConfigNode::ConfigFilter &filter)
00701 {
00702 map<string, string>::const_iterator hasSync = filter.find(EvolutionSyncSourceConfig::m_sourcePropSync.getName());
00703
00704 if (!sync && hasSync != filter.end()) {
00705 m_overrideMode = hasSync->second;
00706 FilterConfigNode::ConfigFilter strippedFilter = filter;
00707 strippedFilter.erase(EvolutionSyncSourceConfig::m_sourcePropSync.getName());
00708 EvolutionSyncConfig::setConfigFilter(sync, strippedFilter);
00709 } else {
00710 EvolutionSyncConfig::setConfigFilter(sync, filter);
00711 }
00712 }
00713
00714 void EvolutionSyncClient::initSources(SourceList &sourceList)
00715 {
00716 set<string> unmatchedSources = m_sources;
00717 list<string> configuredSources = getSyncSources();
00718 BOOST_FOREACH(const string &name, configuredSources) {
00719 boost::shared_ptr<PersistentEvolutionSyncSourceConfig> sc(getSyncSourceConfig(name));
00720
00721
00722 string sync = sc->getSync();
00723 bool enabled = sync != "disabled";
00724 string overrideMode = m_overrideMode;
00725
00726
00727 if (m_sources.size()) {
00728 if (m_sources.find(sc->getName()) != m_sources.end()) {
00729 if (!enabled) {
00730 if (overrideMode.empty()) {
00731 overrideMode = "two-way";
00732 }
00733 enabled = true;
00734 }
00735 unmatchedSources.erase(sc->getName());
00736 } else {
00737 enabled = false;
00738 }
00739 }
00740
00741 if (enabled) {
00742 string url = getSyncURL();
00743 boost::replace_first(url, "https://", "http://");
00744 string changeId = string("sync4jevolution:") + url + "/" + name;
00745 EvolutionSyncSourceParams params(name,
00746 getSyncSourceNodes(name),
00747 changeId);
00748
00749
00750 if (!overrideMode.empty()) {
00751 params.m_nodes.m_configNode->addFilter(EvolutionSyncSourceConfig::m_sourcePropSync.getName(),
00752 overrideMode);
00753 }
00754 EvolutionSyncSource *syncSource =
00755 EvolutionSyncSource::createSource(params);
00756 if (!syncSource) {
00757 throwError(name + ": type unknown" );
00758 }
00759 sourceList.push_back(syncSource);
00760 }
00761 }
00762
00763
00764 if (unmatchedSources.size()) {
00765 throwError(string("no such source(s): ") + boost::join(unmatchedSources, " "));
00766 }
00767 }
00768
00769 int EvolutionSyncClient::sync()
00770 {
00771 int res = 1;
00772
00773 if (!exists()) {
00774 LOG.error("No configuration for server \"%s\" found.", m_server.c_str());
00775 throwError("cannot proceed without configuration");
00776 }
00777
00778
00779 SourceList sourceList(m_server, m_doLogging, *this, m_quiet);
00780 m_sourceListPtr = &sourceList;
00781
00782 try {
00783 sourceList.setLogdir(getLogDir(),
00784 getMaxLogDirs(),
00785 getLogLevel());
00786
00787
00788 #ifdef LOG_HAVE_DEVELOPER
00789 # define LOG_DEVELOPER developer
00790 #else
00791 # define LOG_DEVELOPER debug
00792 #endif
00793 LOG.LOG_DEVELOPER("SyncML server account: %s", getUsername());
00794 LOG.LOG_DEVELOPER("client: SyncEvolution %s for %s",
00795 getSwv(), getDevType());
00796 LOG.LOG_DEVELOPER("device ID: %s", getDevID());
00797 LOG.LOG_DEVELOPER("%s", EDSAbiWrapperDebug());
00798
00799
00800 initSources(sourceList);
00801
00802
00803
00804
00805 EvolutionSyncConfig dummy;
00806 set<string> activeSources = sourceList.getSources();
00807 dummy.copy(*this, &activeSources);
00808
00809
00810
00811 startLoopThread();
00812
00813
00814 checkPassword(*this);
00815 if (getUseProxy()) {
00816 checkProxyPassword(*this);
00817 }
00818 BOOST_FOREACH(EvolutionSyncSource *source, sourceList) {
00819 source->checkPassword(*this);
00820 }
00821
00822
00823 BOOST_FOREACH(EvolutionSyncSource *source, sourceList) {
00824 source->open();
00825 }
00826
00827
00828 prepare(sourceList.getSourceArray());
00829
00830
00831 sourceList.syncPrepare();
00832
00833
00834 res = SyncClient::sync(*this, sourceList.getSourceArray());
00835
00836
00837
00838 flush();
00839
00840 if (res) {
00841 if (getLastErrorCode() && getLastErrorMsg() && getLastErrorMsg()[0]) {
00842 throwError(getLastErrorMsg());
00843 }
00844
00845 throwError("sync failed without an error description, check log");
00846 }
00847
00848
00849 sourceList.syncDone(true);
00850
00851 res = 0;
00852 } catch (const std::exception &ex) {
00853 LOG.error( "%s", ex.what() );
00854
00855
00856 m_sourceListPtr = NULL;
00857 sourceList.syncDone(false);
00858 } catch (...) {
00859 LOG.error( "unknown error" );
00860 m_sourceListPtr = NULL;
00861 sourceList.syncDone(false);
00862 }
00863
00864 m_sourceListPtr = NULL;
00865 return res;
00866 }
00867
00868 void EvolutionSyncClient::prepare(SyncSource **sources) {
00869 if (m_syncMode != SYNC_NONE) {
00870 for (SyncSource **source = sources;
00871 *source;
00872 source++) {
00873 (*source)->setPreferredSyncMode(m_syncMode);
00874 }
00875 }
00876 }
00877
00878 void EvolutionSyncClient::status()
00879 {
00880 EvolutionSyncConfig config(m_server);
00881 if (!exists()) {
00882 LOG.error("No configuration for server \"%s\" found.", m_server.c_str());
00883 throwError("cannot proceed without configuration");
00884 }
00885
00886 SourceList sourceList(m_server, false, *this, false);
00887 initSources(sourceList);
00888 BOOST_FOREACH(EvolutionSyncSource *source, sourceList) {
00889 source->checkPassword(*this);
00890 }
00891 BOOST_FOREACH(EvolutionSyncSource *source, sourceList) {
00892 source->open();
00893 }
00894
00895 sourceList.setLogdir(getLogDir(), 0, LOG_LEVEL_NONE);
00896 LOG.setLevel(LOG_LEVEL_INFO);
00897 string prevLogdir = sourceList.getPrevLogdir();
00898 bool found = access(prevLogdir.c_str(), R_OK|X_OK) == 0;
00899
00900 if (found) {
00901 try {
00902 sourceList.setPath(prevLogdir);
00903 sourceList.dumpDatabases("current");
00904 sourceList.dumpLocalChanges("after", "current");
00905 } catch(const std::exception &ex) {
00906 LOG.error("%s", ex.what());
00907 }
00908 } else {
00909 cerr << "Previous log directory not found.\n";
00910 if (!getLogDir() || !getLogDir()[0]) {
00911 cerr << "Enable the 'logdir' option and synchronize to use this feature.\n";
00912 }
00913 }
00914 }