00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "EvolutionSyncSource.h"
00020 #include "EvolutionSyncClient.h"
00021 #include "SyncEvolutionUtil.h"
00022 #include <common/base/Log.h>
00023
00024 #include <boost/algorithm/string.hpp>
00025 #include <boost/foreach.hpp>
00026
00027 #include <list>
00028 using namespace std;
00029
00030 #include <dlfcn.h>
00031
00032 #ifdef HAVE_EDS
00033 ESource *EvolutionSyncSource::findSource( ESourceList *list, const string &id )
00034 {
00035 for (GSList *g = e_source_list_peek_groups (list); g; g = g->next) {
00036 ESourceGroup *group = E_SOURCE_GROUP (g->data);
00037 GSList *s;
00038 for (s = e_source_group_peek_sources (group); s; s = s->next) {
00039 ESource *source = E_SOURCE (s->data);
00040 char *uri = e_source_get_uri(source);
00041 bool found = id.empty() ||
00042 !id.compare(e_source_peek_name(source)) ||
00043 (uri && !id.compare(uri));
00044 g_free(uri);
00045 if (found) {
00046 return source;
00047 }
00048 }
00049 }
00050 return NULL;
00051 }
00052 #endif // HAVE_EDS
00053
00054 #ifdef HAVE_EDS
00055 void EvolutionSyncSource::throwError(const string &action, GError *gerror)
00056 {
00057 string gerrorstr;
00058 if (gerror) {
00059 gerrorstr += ": ";
00060 gerrorstr += gerror->message;
00061 g_clear_error(&gerror);
00062 } else {
00063 gerrorstr = ": failure";
00064 }
00065
00066 throwError(action + gerrorstr);
00067 }
00068 #endif
00069
00070 void EvolutionSyncSource::throwError(const string &action, int error)
00071 {
00072 throwError(action + ": " + strerror(error));
00073 }
00074
00075 void EvolutionSyncSource::throwError(const string &failure)
00076 {
00077 setFailed(true);
00078 EvolutionSyncClient::throwError(string(getName()) + ": " + failure);
00079 }
00080
00081 void EvolutionSyncSource::resetItems()
00082 {
00083 m_allItems.clear();
00084 m_newItems.clear();
00085 m_updatedItems.clear();
00086 m_deletedItems.clear();
00087 }
00088
00089 void EvolutionSyncSource::handleException()
00090 {
00091 try {
00092 throw;
00093 } catch (std::exception &ex) {
00094 setErrorF(getLastErrorCode() == ERR_NONE ? ERR_UNSPECIFIED : getLastErrorCode(),
00095 "%s", ex.what());
00096 LOG.error("%s", getLastErrorMsg());
00097 setFailed(true);
00098 }
00099 }
00100
00101 SourceRegistry &EvolutionSyncSource::getSourceRegistry()
00102 {
00103 static SourceRegistry sourceRegistry;
00104 return sourceRegistry;
00105 }
00106
00107 RegisterSyncSource::RegisterSyncSource(const string &shortDescr,
00108 bool enabled,
00109 Create_t create,
00110 const string &typeDescr,
00111 const Values &typeValues) :
00112 m_shortDescr(shortDescr),
00113 m_enabled(enabled),
00114 m_create(create),
00115 m_typeDescr(typeDescr),
00116 m_typeValues(typeValues)
00117 {
00118 SourceRegistry ®istry(EvolutionSyncSource::getSourceRegistry());
00119
00120
00121 for(SourceRegistry::iterator it = registry.begin();
00122 it != registry.end();
00123 ++it) {
00124 if ((*it)->m_shortDescr > shortDescr) {
00125 registry.insert(it, this);
00126 return;
00127 }
00128 }
00129 registry.push_back(this);
00130 }
00131
00132 #if 0
00133 static ostream & operator << (ostream &out, const RegisterSyncSource &rhs)
00134 {
00135 out << rhs.m_shortDescr << (rhs.m_enabled ? " (enabled)" : " (disabled)");
00136 }
00137 #endif
00138
00139 EvolutionSyncSource *const RegisterSyncSource::InactiveSource = (EvolutionSyncSource *)1;
00140
00141 TestRegistry &EvolutionSyncSource::getTestRegistry()
00142 {
00143 static TestRegistry testRegistry;
00144 return testRegistry;
00145 }
00146
00147 RegisterSyncSourceTest::RegisterSyncSourceTest(const string &configName, const string &testCaseName) :
00148 m_configName(configName),
00149 m_testCaseName(testCaseName)
00150 {
00151 EvolutionSyncSource::getTestRegistry().push_back(this);
00152 }
00153
00154 static class ScannedModules {
00155 public:
00156 ScannedModules() {
00157 #ifdef ENABLE_MODULES
00158 list<string> *state;
00159
00160
00161 const char *modules[] = {
00162 "syncebook.so.0",
00163 "syncecal.so.0",
00164 "syncsqlite.so.0",
00165 "addressbook.so.0",
00166 NULL
00167 };
00168
00169 for (int i = 0; modules[i]; i++) {
00170 void *dlhandle;
00171
00172
00173
00174
00175 dlhandle = dlopen(modules[i], RTLD_NOW|RTLD_GLOBAL);
00176
00177 state = dlhandle ? &m_available : &m_missing;
00178 state->push_back(modules[i]);
00179 }
00180 #endif
00181 }
00182 list<string> m_available;
00183 list<string> m_missing;
00184 } scannedModules;
00185
00186
00187 EvolutionSyncSource *EvolutionSyncSource::createSource(const EvolutionSyncSourceParams ¶ms, bool error)
00188 {
00189 string sourceTypeString = getSourceTypeString(params.m_nodes);
00190 pair<string, string> sourceType = getSourceType(params.m_nodes);
00191
00192 const SourceRegistry ®istry(getSourceRegistry());
00193 BOOST_FOREACH(const RegisterSyncSource *sourceInfos, registry) {
00194 EvolutionSyncSource *source = sourceInfos->m_create(params);
00195 if (source) {
00196 if (source == RegisterSyncSource::InactiveSource) {
00197 EvolutionSyncClient::throwError(params.m_name + ": access to " + sourceInfos->m_shortDescr +
00198 " not enabled, therefore type = " + sourceTypeString + " not supported");
00199 }
00200 return source;
00201 }
00202 }
00203
00204 if (error) {
00205 string problem = params.m_name + ": type '" + sourceTypeString + "' not supported";
00206 if (scannedModules.m_available.size()) {
00207 problem += " by any of the backends (";
00208 problem += boost::join(scannedModules.m_available, ", ");
00209 problem += ")";
00210 }
00211 if (scannedModules.m_missing.size()) {
00212 problem += ". The following backend(s) were not found: ";
00213 problem += boost::join(scannedModules.m_missing, ", ");
00214 }
00215 EvolutionSyncClient::throwError(problem);
00216 }
00217
00218 return NULL;
00219 }
00220
00221 EvolutionSyncSource *EvolutionSyncSource::createTestingSource(const string &name, const string &type, bool error,
00222 const char *prefix)
00223 {
00224 EvolutionSyncConfig config("testing");
00225 SyncSourceNodes nodes = config.getSyncSourceNodes(name);
00226 EvolutionSyncSourceParams params(name, nodes, "");
00227 PersistentEvolutionSyncSourceConfig sourceconfig(name, nodes);
00228 sourceconfig.setSourceType(type);
00229 if (prefix) {
00230 sourceconfig.setDatabaseID(string(prefix) + name + "_1");
00231 }
00232 return createSource(params, error);
00233 }
00234
00235 int EvolutionSyncSource::beginSync() throw()
00236 {
00237 string buffer;
00238 buffer += getName();
00239 buffer += ": sync mode is ";
00240 SyncMode mode = getSyncMode();
00241 buffer += mode == SYNC_SLOW ? "'slow'" :
00242 mode == SYNC_TWO_WAY ? "'two-way'" :
00243 mode == SYNC_REFRESH_FROM_SERVER ? "'refresh from server'" :
00244 mode == SYNC_REFRESH_FROM_CLIENT ? "'refresh from client'" :
00245 mode == SYNC_ONE_WAY_FROM_SERVER ? "'one-way from server'" :
00246 mode == SYNC_ONE_WAY_FROM_CLIENT ? "'one-way from client'" :
00247 mode == SYNC_NONE ? "'none' (for debugging)" :
00248 "???";
00249 LOG.info( buffer.c_str() );
00250
00251
00252
00253 EvolutionSyncClient::startLoopThread();
00254
00255 try {
00256
00257
00258
00259
00260 getConfig().setLast(0);
00261
00262 const char *error = getenv("SYNCEVOLUTION_BEGIN_SYNC_ERROR");
00263 if (error && strstr(error, getName())) {
00264 EvolutionSyncClient::throwError("artificial error in beginSync()");
00265 }
00266
00267
00268 m_isModified = false;
00269 m_allItems.clear();
00270 m_newItems.clear();
00271 m_updatedItems.clear();
00272 m_deletedItems.clear();
00273
00274
00275 bool needAll = false;
00276 bool needPartial = false;
00277 bool deleteLocal = false;
00278 switch (mode) {
00279 case SYNC_SLOW:
00280 needAll = true;
00281 m_isModified = true;
00282 break;
00283 case SYNC_ONE_WAY_FROM_CLIENT:
00284 case SYNC_TWO_WAY:
00285 needPartial = true;
00286 break;
00287 case SYNC_REFRESH_FROM_SERVER:
00288 deleteLocal = true;
00289 m_isModified = true;
00290 break;
00291 case SYNC_REFRESH_FROM_CLIENT:
00292 needAll = true;
00293 m_isModified = true;
00294 break;
00295 case SYNC_NONE:
00296
00297 needAll = needPartial = true;
00298 break;
00299 case SYNC_ONE_WAY_FROM_SERVER:
00300
00301 break;
00302 default:
00303 EvolutionSyncClient::throwError("unsupported sync mode, valid are only: slow, two-way, refresh");
00304 break;
00305 }
00306
00307 beginSyncThrow(needAll, needPartial, deleteLocal);
00308 } catch( ... ) {
00309 handleException();
00310 return 1;
00311 }
00312 return 0;
00313 }
00314
00315 int EvolutionSyncSource::endSync() throw()
00316 {
00317 try {
00318 endSyncThrow();
00319 } catch ( ... ) {
00320 handleException();
00321 }
00322
00323
00324
00325
00326
00327 return 0;
00328 }
00329
00330 void EvolutionSyncSource::setItemStatus(const char *key, int status) throw()
00331 {
00332 try {
00333
00334 setItemStatusThrow(key, status);
00335 } catch (...) {
00336 handleException();
00337 }
00338 }
00339
00340 int EvolutionSyncSource::addItem(SyncItem& item) throw()
00341 {
00342 return processItem("add", &EvolutionSyncSource::addItemThrow, item, true);
00343 }
00344
00345 int EvolutionSyncSource::updateItem(SyncItem& item) throw()
00346 {
00347 return processItem("update", &EvolutionSyncSource::updateItemThrow, item, true);
00348 }
00349
00350 int EvolutionSyncSource::deleteItem(SyncItem& item) throw()
00351 {
00352 return processItem("delete", &EvolutionSyncSource::deleteItemThrow, item, false);
00353 }
00354
00355 int EvolutionSyncSource::removeAllItems() throw()
00356 {
00357 int status = 0;
00358
00359 try {
00360 BOOST_FOREACH(const string &key, m_allItems) {
00361 SyncItem item;
00362 item.setKey(key.c_str());
00363 logItem(item, "delete all items");
00364 deleteItemThrow(item);
00365 m_isModified = true;
00366 }
00367 } catch (...) {
00368 handleException();
00369 status = 1;
00370 }
00371 return status;
00372 }
00373
00374 int EvolutionSyncSource::processItem(const char *action,
00375 int (EvolutionSyncSource::*func)(SyncItem& item),
00376 SyncItem& item,
00377 bool needData) throw()
00378 {
00379 int status = STC_COMMAND_FAILED;
00380
00381 try {
00382 logItem(item, action);
00383 if (needData && (item.getDataSize() < 0 || !item.getData())) {
00384
00385
00386
00387 logItem(item, "ignored due to missing data");
00388 status = STC_OK;
00389 } else {
00390 status = (this->*func)(item);
00391 }
00392 m_isModified = true;
00393 } catch (...) {
00394 handleException();
00395 }
00396 databaseModified();
00397 return status;
00398 }
00399
00400 void EvolutionSyncSource::setItemStatusThrow(const char *key, int status)
00401 {
00402 switch (status) {
00403 case STC_ALREADY_EXISTS:
00404
00405 break;
00406 default:
00407 if (status < 200 || status > 300) {
00408 LOG.error("%s: unexpected SyncML status response %d for item %.80s\n",
00409 getName(), status, key);
00410 setFailed(true);
00411 }
00412 }
00413 }
00414
00415 void EvolutionSyncSource::sleepSinceModification(int seconds)
00416 {
00417 time_t current = time(NULL);
00418 while (current - m_modTimeStamp < seconds) {
00419 sleep(seconds - (current - m_modTimeStamp));
00420 current = time(NULL);
00421 }
00422 }
00423
00424 void EvolutionSyncSource::databaseModified()
00425 {
00426 m_modTimeStamp = time(NULL);
00427 }
00428
00429 void EvolutionSyncSource::logItemUtil(const string data, const string &mimeType, const string &mimeVersion,
00430 const string &uid, const string &info, bool debug)
00431 {
00432 if (LOG.getLevel() >= (debug ? LOG_LEVEL_DEBUG : LOG_LEVEL_INFO)) {
00433 string name;
00434
00435 if (mimeType == "text/plain") {
00436 size_t eol = data.find('\n');
00437 if (eol != data.npos) {
00438 name.assign(data, 0, eol);
00439 } else {
00440 name = data;
00441 }
00442 } else {
00443
00444
00445
00446 string prop;
00447
00448 if (mimeType == "text/vcard" ||
00449 mimeType == "text/x-vcard") {
00450 prop = "FN";
00451 } else if (mimeType == "text/calendar" ||
00452 mimeType == "text/x-calendar") {
00453 prop = "SUMMARY";
00454 }
00455
00456 if (prop.size()) {
00457 size_t start = 0;
00458
00459 while (start < data.size()) {
00460 start = data.find(prop, start);
00461 if (start == data.npos) {
00462 break;
00463 }
00464
00465
00466 if (start > 0 && data[start - 1] == '\n' &&
00467 start + prop.size() < data.size() &&
00468 (data[start + prop.size()] == ';' ||
00469 data[start + prop.size()] == ':')) {
00470 start = data.find(':', start);
00471 if (start != data.npos) {
00472 start++;
00473 size_t end = data.find_first_of("\n\r", start);
00474 name.assign(data,
00475 start,
00476 end == data.npos ? data.npos : (end - start));
00477 }
00478 break;
00479 } else {
00480 start += prop.size();
00481 }
00482 }
00483 }
00484 }
00485
00486 if (name.size()) {
00487 (LOG.*(debug ? &Log::debug : &Log::info))("%s: %s %s",
00488 getName(),
00489 name.c_str(),
00490 info.c_str());
00491 } else {
00492 (LOG.*(debug ? &Log::debug : &Log::info))("%s: LUID %s %s",
00493 getName(),
00494 uid.c_str(),
00495 info.c_str());
00496 }
00497 }
00498 }
00499
00500 SyncItem *EvolutionSyncSource::Items::start()
00501 {
00502 m_it = begin();
00503 LOG.debug("start scanning %s items", m_type.c_str());
00504 return iterate();
00505 }
00506
00507 SyncItem *EvolutionSyncSource::Items::iterate()
00508 {
00509 if (m_it != end()) {
00510 const string &uid( *m_it );
00511 LOG.debug("next %s item: %s", m_type.c_str(), uid.c_str());
00512 ++m_it;
00513 if (&m_source.m_deletedItems == this) {
00514
00515
00516 SyncItem *item = new SyncItem( uid.c_str() );
00517 item->setDataType(m_source.getMimeType());
00518 return item;
00519 } else {
00520
00521 try {
00522 cxxptr<SyncItem> item(m_source.createItem(uid));
00523 if (item) {
00524 item->setState(m_state);
00525 }
00526 return item.release();
00527 } catch(...) {
00528 m_source.handleException();
00529 return NULL;
00530 }
00531 }
00532 } else {
00533 return NULL;
00534 }
00535 }
00536
00537 bool EvolutionSyncSource::Items::addItem(const string &uid) {
00538 pair<iterator, bool> res = insert(uid);
00539 if (res.second) {
00540 m_source.logItem(uid, m_type, true);
00541 }
00542 return res.second;
00543 }