00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include <memory>
00020 #include <map>
00021 #include <sstream>
00022 #include <list>
00023 using namespace std;
00024
00025 #include "config.h"
00026 #include "EvolutionSyncSource.h"
00027
00028 #ifdef ENABLE_EBOOK
00029
00030 #include "EvolutionSyncClient.h"
00031 #include "EvolutionContactSource.h"
00032 #include "SyncEvolutionUtil.h"
00033
00034 #include <common/base/Log.h>
00035 #include "vocl/VConverter.h"
00036 using namespace vocl;
00037
00038 #include <boost/algorithm/string.hpp>
00039 #include <boost/foreach.hpp>
00040
00041 class unrefEBookChanges {
00042 public:
00043
00044 static void unref(GList *pointer) {
00045 if (pointer) {
00046 GList *next = pointer;
00047 do {
00048 EBookChange *ebc = (EBookChange *)next->data;
00049 g_object_unref(ebc->contact);
00050 g_free(next->data);
00051 next = next->next;
00052 } while (next);
00053 g_list_free(pointer);
00054 }
00055 }
00056 };
00057
00058
00059 const EvolutionContactSource::extensions EvolutionContactSource::m_vcardExtensions;
00060 const EvolutionContactSource::unique EvolutionContactSource::m_uniqueProperties;
00061
00062 EvolutionContactSource::EvolutionContactSource(const EvolutionSyncSourceParams ¶ms,
00063 EVCardFormat vcardFormat) :
00064 EvolutionSyncSource(params),
00065 m_vcardFormat(vcardFormat)
00066 {
00067 }
00068
00069 EvolutionContactSource::EvolutionContactSource( const EvolutionContactSource &other ) :
00070 EvolutionSyncSource( other ),
00071 m_vcardFormat( other.m_vcardFormat )
00072 {
00073 }
00074
00075 EvolutionSyncSource::Databases EvolutionContactSource::getDatabases()
00076 {
00077 ESourceList *sources = NULL;
00078
00079 if (!e_book_get_addressbooks(&sources, NULL)) {
00080 EvolutionSyncClient::throwError("unable to access address books");
00081 }
00082
00083 Databases result;
00084 bool first = true;
00085 for (GSList *g = e_source_list_peek_groups (sources); g; g = g->next) {
00086 ESourceGroup *group = E_SOURCE_GROUP (g->data);
00087 for (GSList *s = e_source_group_peek_sources (group); s; s = s->next) {
00088 ESource *source = E_SOURCE (s->data);
00089 eptr<char> uri(e_source_get_uri(source));
00090 result.push_back(Database(e_source_peek_name(source),
00091 uri ? uri.get() : "",
00092 first));
00093 first = false;
00094 }
00095 }
00096
00097
00098 if (!result.size()) {
00099 eptr<EBook, GObject> book;
00100 GError *gerror = NULL;
00101 const char *name;
00102
00103 name = "<<system>>";
00104 book = e_book_new_system_addressbook (&gerror);
00105 g_clear_error(&gerror);
00106 if (!book) {
00107 name = "<<default>>";
00108 book = e_book_new_default_addressbook (&gerror);
00109 }
00110 g_clear_error(&gerror);
00111
00112 if (book) {
00113 const char *uri = e_book_get_uri (book);
00114 result.push_back(Database(name, uri, true));
00115 }
00116 }
00117
00118 return result;
00119 }
00120
00121 void EvolutionContactSource::open()
00122 {
00123 ESourceList *sources;
00124 if (!e_book_get_addressbooks(&sources, NULL)) {
00125 throwError("unable to access address books");
00126 }
00127
00128 GError *gerror = NULL;
00129 string id = getDatabaseID();
00130 ESource *source = findSource(sources, id);
00131 bool onlyIfExists = true;
00132 if (!source) {
00133
00134
00135 if (id.empty() || id == "<<system>>") {
00136 m_addressbook.set( e_book_new_system_addressbook (&gerror), "system address book" );
00137 } else if (id.empty() || id == "<<default>>") {
00138 m_addressbook.set( e_book_new_default_addressbook (&gerror), "default address book" );
00139 } else if (boost::starts_with(id, "file://")) {
00140 m_addressbook.set(e_book_new_from_uri(id.c_str(), &gerror), "creating address book");
00141 } else {
00142 throwError(string(getName()) + ": no such address book: '" + id + "'");
00143 }
00144 onlyIfExists = false;
00145 } else {
00146 m_addressbook.set( e_book_new( source, &gerror ), "address book" );
00147 }
00148
00149 if (!e_book_open( m_addressbook, onlyIfExists, &gerror) ) {
00150
00151 g_clear_error(&gerror);
00152 sleep(5);
00153 if (!e_book_open( m_addressbook, onlyIfExists, &gerror) ) {
00154 throwError( "opening address book", gerror );
00155 }
00156 }
00157
00158
00159
00160
00161 const char *user = getUser(),
00162 *passwd = getPassword();
00163 if (user && user[0] || passwd && passwd[0]) {
00164 GList *authmethod;
00165 if (!e_book_get_supported_auth_methods(m_addressbook, &authmethod, &gerror)) {
00166 throwError("getting authentication methods", gerror );
00167 }
00168 while (authmethod) {
00169 const char *method = (const char *)authmethod->data;
00170 LOG.debug("%s: trying authentication method \"%s\", user %s, password %s",
00171 getName(), method,
00172 user && user[0] ? "configured" : "not configured",
00173 passwd && passwd[0] ? "configured" : "not configured");
00174 if (e_book_authenticate_user(m_addressbook,
00175 user ? user : "",
00176 passwd ? passwd : "",
00177 method,
00178 &gerror)) {
00179 LOG.debug("%s: authentication succeeded", getName());
00180 break;
00181 } else {
00182 LOG.error("%s: authentication failed: %s",
00183 getName(), gerror->message);
00184 g_clear_error(&gerror);
00185 }
00186 authmethod = authmethod->next;
00187 }
00188 }
00189
00190 g_signal_connect_after(m_addressbook,
00191 "backend-died",
00192 G_CALLBACK(EvolutionSyncClient::fatalError),
00193 (void *)"Evolution Data Server has died unexpectedly, contacts no longer available.");
00194 }
00195
00196 void EvolutionContactSource::beginSyncThrow(bool needAll,
00197 bool needPartial,
00198 bool deleteLocal)
00199 {
00200 GError *gerror = NULL;
00201
00202 eptr<EBookQuery> deleteItemsQuery;
00203 if (deleteLocal) {
00204 deleteItemsQuery.set( e_book_query_any_field_contains(""), "query" );
00205 }
00206 #ifdef ENABLE_MAEMO_OSSO_CONTACT_STATE
00207 else {
00208 deleteItemsQuery.set( e_book_query_vcard_field_exists("X-OSSO-CONTACT-STATE"), "query" );
00209 }
00210 #endif
00211
00212 if (deleteItemsQuery) {
00213 GList *nextItem;
00214
00215 if (!e_book_get_contacts( m_addressbook, deleteItemsQuery, &nextItem, &gerror )) {
00216 throwError( "reading items to be deleted", gerror );
00217 }
00218 eptr<GList> listptr(nextItem);
00219 for (;nextItem; nextItem = nextItem->next) {
00220 const char *uid = (const char *)e_contact_get_const(E_CONTACT(nextItem->data),
00221 E_CONTACT_UID);
00222 #ifdef ENABLE_MAEMO_OSSO_CONTACT_STATE
00223 if (!deleteLocal) {
00224 GList *nextState = (GList *)e_contact_get(E_CONTACT(nextItem->data),
00225 E_CONTACT_OSSO_CONTACT_STATE);
00226 bool deleted = false;
00227 while (nextState) {
00228 LOG.debug("checking X-OSSO-CONTACT-STATE %p of uid %s",
00229 nextState->data, uid);
00230 if ((char *)nextState->data < (char *)1024) {
00231 LOG.info("broken X-OSSO-CONTACT-STATE %p, please report this to the SyncEvolution developer",
00232 nextState->data);
00233 } else {
00234 LOG.debug("X-OSSO-CONTACT-STATE %p = %s",
00235 nextState->data, (char *)nextState->data);
00236 if (nextState->data && !strcmp((char *)nextState->data, "DELETED")) {
00237 deleted = true;
00238 }
00239 }
00240 nextState = nextState->next;
00241 }
00242 if (!deleted) {
00243 continue;
00244 }
00245 logItem(string(uid), "deleting item scheduled for removal", true);
00246 if (needPartial) {
00247
00248
00249
00250 m_deletedItems.addItem(uid);
00251 }
00252 }
00253 #endif
00254 if (!e_book_remove_contact( m_addressbook, uid, &gerror ) ) {
00255 throwError( string( "deleting contact " ) + uid,
00256 gerror );
00257 }
00258 }
00259 }
00260
00261 if (needAll) {
00262 eptr<EBookQuery> allItemsQuery( e_book_query_any_field_contains(""), "query" );
00263 GList *nextItem;
00264 if (!e_book_get_contacts( m_addressbook, allItemsQuery, &nextItem, &gerror )) {
00265 throwError( "reading all items", gerror );
00266 }
00267 eptr<GList> listptr(nextItem);
00268 while (nextItem) {
00269 const char *uid = (const char *)e_contact_get_const(E_CONTACT(nextItem->data),
00270 E_CONTACT_UID);
00271 m_allItems.addItem(uid);
00272 nextItem = nextItem->next;
00273 }
00274 }
00275
00276 if (needPartial) {
00277 GList *nextItem;
00278 if (!e_book_get_changes( m_addressbook, (char *)m_changeId.c_str(), &nextItem, &gerror )) {
00279 throwError( "reading changes", gerror );
00280 }
00281 eptr<GList, GList, unrefEBookChanges> listptr(nextItem);
00282 while (nextItem) {
00283 EBookChange *ebc = (EBookChange *)nextItem->data;
00284
00285 if (ebc->contact) {
00286 const char *uid = (const char *)e_contact_get_const( ebc->contact, E_CONTACT_UID );
00287
00288 if (uid) {
00289 switch (ebc->change_type) {
00290 case E_BOOK_CHANGE_CARD_ADDED:
00291 m_newItems.addItem(uid);
00292 break;
00293 case E_BOOK_CHANGE_CARD_MODIFIED:
00294 m_updatedItems.addItem(uid);
00295 break;
00296 case E_BOOK_CHANGE_CARD_DELETED:
00297 m_deletedItems.addItem(uid);
00298 break;
00299 }
00300 }
00301 }
00302 nextItem = nextItem->next;
00303 }
00304 }
00305 }
00306
00307 void EvolutionContactSource::endSyncThrow()
00308 {
00309 if (m_isModified) {
00310 GError *gerror = NULL;
00311 GList *nextItem;
00312
00313 if (!e_book_get_changes( m_addressbook, (char *)m_changeId.c_str(), &nextItem, &gerror )) {
00314 throwError( "reading changes", gerror );
00315 }
00316 eptr<GList, GList, unrefEBookChanges> listptr(nextItem);
00317 }
00318 resetItems();
00319 m_isModified = false;
00320 }
00321
00322 void EvolutionContactSource::close()
00323 {
00324 endSyncThrow();
00325 m_addressbook = NULL;
00326 }
00327
00328 void EvolutionContactSource::exportData(ostream &out)
00329 {
00330 eptr<EBookQuery> allItemsQuery( e_book_query_any_field_contains(""), "query" );
00331 GList *nextItem;
00332 GError *gerror = NULL;
00333 if (!e_book_get_contacts( m_addressbook, allItemsQuery, &nextItem, &gerror )) {
00334 throwError( "reading all items", gerror );
00335 }
00336 eptr<GList> listptr(nextItem);
00337 while (nextItem) {
00338 eptr<char> vcardstr(e_vcard_to_string(&E_CONTACT(nextItem->data)->parent,
00339 EVC_FORMAT_VCARD_30));
00340
00341 if (!(const char *)vcardstr) {
00342 throwError("could not convert contact into string");
00343 }
00344 out << (const char *)vcardstr << "\r\n\r\n";
00345 nextItem = nextItem->next;
00346 }
00347 }
00348
00349 SyncItem *EvolutionContactSource::createItem(const string &uid)
00350 {
00351 logItem( uid, "extracting from EV", true );
00352
00353 EContact *contact;
00354 GError *gerror = NULL;
00355 if (! e_book_get_contact( m_addressbook,
00356 uid.c_str(),
00357 &contact,
00358 &gerror ) ) {
00359 throwError( string( "reading contact " ) + uid,
00360 gerror );
00361 }
00362 eptr<EContact, GObject> contactptr( contact, "contact" );
00363 eptr<char> vcardstr(e_vcard_to_string( &contactptr->parent,
00364 EVC_FORMAT_VCARD_30 ) );
00365 if (!vcardstr) {
00366 throwError(string("failure extracting contact from Evolution " ) + uid);
00367 }
00368 LOG.debug("%s", vcardstr.get());
00369
00370 std::auto_ptr<VObject> vobj(VConverter::parse(vcardstr));
00371 if (vobj.get() == 0) {
00372 throwError(string("failure parsing contact " ) + uid);
00373 }
00374
00375 vobj->toNativeEncoding();
00376
00377 for (int index = vobj->propertiesCount() - 1;
00378 index >= 0;
00379 index--) {
00380 VProperty *vprop = vobj->getProperty(index);
00381
00382
00383
00384
00385
00386 bool parcel = false;
00387
00388 int param = 0;
00389 while (param < vprop->parameterCount()) {
00390 if (!strcasecmp(vprop->getParameter(param), "TYPE") &&
00391 !strcasecmp(vprop->getParameterValue(param), "OTHER")) {
00392 vprop->removeParameter(param);
00393 if (!strcasecmp(vprop->getName(), "ADR")) {
00394 parcel = true;
00395 }
00396 } else {
00397 param++;
00398 }
00399 }
00400
00401 if (parcel) {
00402 vprop->addParameter("TYPE", "PARCEL");
00403 }
00404 }
00405
00406
00407 if (m_vcardFormat == EVC_FORMAT_VCARD_21) {
00408 LOG.debug("convert to 2.1");
00409
00410
00411
00412 for (int index = vobj->propertiesCount() - 1;
00413 index >= 0;
00414 index--) {
00415 VProperty *vprop = vobj->getProperty(index);
00416 string name = vprop->getName();
00417 if (m_vcardExtensions.find(name) != m_vcardExtensions.end()) {
00418 name = m_vcardExtensions.prefix + name;
00419 vprop->setName(name.c_str());
00420 }
00421
00422
00423 char *encoding = vprop->getParameterValue("ENCODING");
00424 if (encoding &&
00425 !strcasecmp("B", encoding)) {
00426 vprop->removeParameter("ENCODING");
00427 vprop->addParameter("ENCODING", "BASE64");
00428 }
00429
00430
00431
00432
00433
00434
00435 if (!encoding) {
00436 char *value = vprop->getValue();
00437
00438 if (value &&
00439 value[0] &&
00440 value[strlen(value)-1] == '=') {
00441 vprop->addParameter("ENCODING", "QUOTED-PRINTABLE");
00442 }
00443 }
00444
00445
00446
00447 list<string> types;
00448 while (true) {
00449 char *type = vprop->getParameterValue("TYPE");
00450 if (!type) {
00451 break;
00452 }
00453 StringBuffer buff(type);
00454
00455 char *token = strtok((char *)buff.c_str(), ",");
00456 while (token != NULL) {
00457 types.push_back(token);
00458 token = strtok(NULL, ",");
00459 }
00460 vprop->removeParameter("TYPE");
00461 }
00462 BOOST_FOREACH(const string &type, types) {
00463 vprop->addParameter("TYPE", type.c_str());
00464 }
00465
00466
00467
00468 list< pair<string, string> > parameters;
00469 while (vprop->parameterCount() > 0) {
00470 const char *param = vprop->getParameter(0);
00471 const char *value = vprop->getParameterValue(0);
00472 if (!param || !value) {
00473 break;
00474 }
00475 parameters.push_back(pair<string, string>(boost::to_upper_copy(string(param)), boost::to_upper_copy(string(value))));
00476 vprop->removeParameter(0);
00477 }
00478 while (parameters.size() > 0) {
00479 pair<string, string> param_value = parameters.front();
00480 vprop->addParameter(param_value.first.c_str(), param_value.second.c_str());
00481 parameters.pop_front();
00482 }
00483 }
00484
00485 vobj->setVersion("2.1");
00486 VProperty *vprop = vobj->getProperty("VERSION");
00487 vprop->setValue("2.1");
00488 }
00489
00490 vobj->fromNativeEncoding();
00491
00492 arrayptr<char> finalstr(vobj->toString(), "VOCL string");
00493 LOG.debug("after conversion:");
00494 LOG.debug("%s", (char *)finalstr);
00495
00496 auto_ptr<SyncItem> item( new SyncItem( uid.c_str() ) );
00497 item->setData( (char *)finalstr, strlen(finalstr) );
00498 item->setDataType( getMimeType() );
00499 item->setModificationTime( 0 );
00500
00501 return item.release();
00502 }
00503
00504 string EvolutionContactSource::preparseVCard(SyncItem& item)
00505 {
00506 string data = (const char *)item.getData();
00507
00508
00509
00510 LOG.debug("%s", data.c_str());
00511 std::auto_ptr<VObject> vobj(VConverter::parse((char *)data.c_str()));
00512 if (vobj.get() == 0) {
00513 throwError(string("failure parsing contact " ) + item.getKey());
00514 }
00515 vobj->toNativeEncoding();
00516
00517
00518
00519
00520
00521
00522
00523
00524
00525 set<string> found;
00526
00527 #ifdef SET_UI_SLOT
00528 class slots : public map< string, set<string> > {
00529 public:
00530 slots() {
00531 insert(value_type(string("ADR"), set<string>()));
00532 insert(value_type(string("EMAIL"), set<string>()));
00533 insert(value_type(string("TEL"), set<string>()));
00534 }
00535 string assignFree(string type) {
00536 int slot = 1;
00537 set<string> &used((*this)[type]);
00538
00539 while (true) {
00540 stringstream buffer;
00541 buffer << slot;
00542 string slotstr = buffer.str();
00543 if (used.find(slotstr) == used.end()) {
00544 used.insert(slotstr);
00545 return slotstr;
00546 }
00547 slot++;
00548 }
00549 }
00550 } usedSlots;
00551 #endif
00552
00553 for (int index = vobj->propertiesCount() - 1;
00554 index >= 0;
00555 index--) {
00556 VProperty *vprop = vobj->getProperty(index);
00557 string name = vprop->getName();
00558 if (name.size() > m_vcardExtensions.prefix.size() &&
00559 !name.compare(0, m_vcardExtensions.prefix.size(), m_vcardExtensions.prefix)) {
00560 name = name.substr(m_vcardExtensions.prefix.size());
00561 vprop->setName(name.c_str());
00562 } else if (name == "ADR" || name == "EMAIL" || name == "TEL") {
00563 const char *type = vprop->getParameterValue("TYPE");
00564 bool isOther = false;
00565 if (type) {
00566 if (!strcasecmp(type, "PARCEL")) {
00567
00568
00569 vprop->removeParameter("TYPE");
00570 isOther = true;
00571 } else if (!strcasecmp(type, "PREF,VOICE")) {
00572
00573
00574 vprop->removeParameter("TYPE");
00575 vprop->addParameter("TYPE", "PREF");
00576 } else if (strchr(type, ',')) {
00577
00578
00579 string buffer = type, value;
00580 size_t start = 0, end;
00581 vprop->removeParameter("TYPE");
00582 while ((end = buffer.find(',', start)) != buffer.npos) {
00583 value = buffer.substr(start, end - start);
00584 vprop->addParameter("TYPE", value.c_str());
00585 start = end + 1;
00586 }
00587 value = buffer.substr(start);
00588 vprop->addParameter("TYPE", value.c_str());
00589 }
00590 }
00591
00592
00593 if (!vprop->containsParameter("TYPE") &&
00594
00595
00596 !vprop->containsParameter("CELL") &&
00597 !vprop->containsParameter("CAR") &&
00598 !vprop->containsParameter("PREF") &&
00599 !vprop->containsParameter("FAX") &&
00600 !vprop->containsParameter("VOICE") &&
00601 !vprop->containsParameter("MSG") &&
00602 !vprop->containsParameter("BBS") &&
00603 !vprop->containsParameter("MODEM") &&
00604 !vprop->containsParameter("ISDN") &&
00605 !vprop->containsParameter("VIDEO") &&
00606 !vprop->containsParameter("PAGER") &&
00607
00608
00609 !vprop->containsParameter("DOM") &&
00610 !vprop->containsParameter("INTL") &&
00611 !vprop->containsParameter("POSTAL") &&
00612 !vprop->containsParameter("PARCEL") &&
00613
00614
00615 !vprop->containsParameter("AOL") &&
00616 !vprop->containsParameter("AppleLink") &&
00617 !vprop->containsParameter("ATTMail") &&
00618 !vprop->containsParameter("CIS") &&
00619 !vprop->containsParameter("eWorld") &&
00620 !vprop->containsParameter("INTERNET") &&
00621 !vprop->containsParameter("IBMMail") &&
00622 !vprop->containsParameter("MCIMail") &&
00623 !vprop->containsParameter("POWERSHARE") &&
00624 !vprop->containsParameter("PRODIGY") &&
00625 !vprop->containsParameter("TLX") &&
00626 !vprop->containsParameter("X400") &&
00627
00628
00629 !vprop->containsParameter("HOME") &&
00630 !vprop->containsParameter("WORK")) {
00631 vprop->addParameter("TYPE", isOther ? "OTHER" : "HOME");
00632 }
00633
00634 #ifdef SET_UI_SLOT
00635
00636 const char *slot = vprop->getParameterValue("X-EVOLUTION-UI-SLOT");
00637 if (slot) {
00638 usedSlots[name].insert(slot);
00639 }
00640 #endif
00641 }
00642
00643
00644 char *encoding = vprop->getParameterValue("ENCODING");
00645 if (encoding &&
00646 !strcasecmp("BASE64", encoding)) {
00647 vprop->removeParameter("ENCODING");
00648 vprop->addParameter("ENCODING", "B");
00649 }
00650
00651 if (m_uniqueProperties.find(name) != m_uniqueProperties.end()) {
00652
00653 if (found.find(name) != found.end()) {
00654
00655 vobj->removeProperty(index);
00656 } else {
00657
00658 found.insert(name);
00659 }
00660 }
00661 }
00662
00663 #ifdef SET_UI_SLOT
00664
00665 for (int index = 0;
00666 index < vobj->propertiesCount();
00667 index++) {
00668 VProperty *vprop = vobj->getProperty(index);
00669 string name = vprop->getName();
00670 if (name == "EMAIL" || name == "TEL") {
00671 const char *slot = vprop->getParameterValue("X-EVOLUTION-UI-SLOT");
00672
00673 if (!slot) {
00674 string freeslot = usedSlots.assignFree(name);
00675 vprop->addParameter("X-EVOLUTION-UI-SLOT", freeslot.c_str());
00676 }
00677 }
00678 }
00679 #endif
00680
00681 vobj->setVersion("3.0");
00682 VProperty *vprop = vobj->getProperty("VERSION");
00683 vprop->setValue("3.0");
00684 vobj->fromNativeEncoding();
00685 arrayptr<char> voclstr(vobj->toString(), "VOCL string");
00686 data = (char *)voclstr;
00687 LOG.debug("after conversion to 3.0:");
00688 LOG.debug("%s", data.c_str());
00689 return data;
00690 }
00691
00692 void EvolutionContactSource::setItemStatusThrow(const char *key, int status)
00693 {
00694 switch (status) {
00695 case STC_CONFLICT_RESOLVED_WITH_SERVER_DATA: {
00696
00697
00698 LOG.error("%s: contact %s: conflict, will be replaced by server contact - create copy",
00699 getName(), key);
00700
00701 EContact *contact;
00702 GError *gerror = NULL;
00703 if (! e_book_get_contact( m_addressbook,
00704 key,
00705 &contact,
00706 &gerror ) ) {
00707 LOG.error("%s: item %.80s: reading original for copy failed",
00708 getName(), key);
00709 break;
00710 }
00711 eptr<EContact, GObject> contactptr( contact, "contact" );
00712 EContact *copy = e_contact_duplicate(contact);
00713 eptr<EContact, GObject> contactcopyptr(copy);
00714 if(!copy ||
00715 ! e_book_add_contact(m_addressbook,
00716 copy,
00717 &gerror)) {
00718 LOG.error("%s: item %.80s: making copy failed",
00719 getName(), key);
00720 break;
00721 }
00722 break;
00723 }
00724 default:
00725 EvolutionSyncSource::setItemStatusThrow(key, status);
00726 break;
00727 }
00728 }
00729
00730 int EvolutionContactSource::addItemThrow(SyncItem& item)
00731 {
00732 int status = STC_OK;
00733 string data;
00734 if( strcmp(item.getDataType(), "raw" ) ) {
00735 data = preparseVCard(item);
00736 } else {
00737 data = (const char *)item.getData();
00738 }
00739 eptr<EContact, GObject> contact(e_contact_new_from_vcard(data.c_str()));
00740 if( contact ) {
00741 GError *gerror = NULL;
00742 e_contact_set(contact, E_CONTACT_UID, NULL);
00743 if (e_book_add_contact(m_addressbook, contact, &gerror)) {
00744 item.setKey( (const char *)e_contact_get_const( contact, E_CONTACT_UID ) );
00745 } else {
00746 throwError( "storing new contact", gerror );
00747 }
00748 } else {
00749 throwError(string("failure parsing vcard " ) + data);
00750 }
00751 return status;
00752 }
00753
00754 int EvolutionContactSource::updateItemThrow(SyncItem& item)
00755 {
00756 int status = STC_OK;
00757 string data = preparseVCard(item);
00758 eptr<EContact, GObject> contact(e_contact_new_from_vcard(data.c_str()));
00759 if( contact ) {
00760 GError *gerror = NULL;
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771 e_contact_set( contact, E_CONTACT_UID, (void *)item.getKey() );
00772 if ( e_book_commit_contact(m_addressbook, contact, &gerror) ) {
00773 const char *uid = (const char *)e_contact_get_const(contact, E_CONTACT_UID);
00774 if (uid) {
00775 item.setKey( uid );
00776 }
00777
00778 #if 0
00779 EContact *refresh_contact;
00780 if (! e_book_get_contact( m_addressbook,
00781 uid,
00782 &refresh_contact,
00783 &gerror ) ) {
00784 throwError( string( "reading refresh contact " ) + uid,
00785 gerror );
00786 }
00787 eptr<EContact, GObject> contactptr( refresh_contact, "contact" );
00788 string nick = (const char *)e_contact_get_const(refresh_contact, E_CONTACT_NICKNAME);
00789 string nick_mod = nick + "_";
00790 e_contact_set(refresh_contact, E_CONTACT_NICKNAME, (void *)nick_mod.c_str());
00791 e_book_commit_contact(m_addressbook, refresh_contact, &gerror);
00792 e_contact_set(refresh_contact, E_CONTACT_NICKNAME, (void *)nick.c_str());
00793 e_book_commit_contact(m_addressbook, refresh_contact, &gerror);
00794 #endif
00795 } else {
00796 throwError( string( "updating contact " ) + item.getKey(), gerror );
00797 }
00798 } else {
00799 throwError(string("failure parsing vcard " ) + data);
00800 }
00801 return status;
00802 }
00803
00804 int EvolutionContactSource::deleteItemThrow(SyncItem& item)
00805 {
00806 int status = STC_OK;
00807 GError *gerror = NULL;
00808 if (!e_book_remove_contact( m_addressbook, item.getKey(), &gerror ) ) {
00809 if (gerror->domain == E_BOOK_ERROR &&
00810 gerror->code == E_BOOK_ERROR_CONTACT_NOT_FOUND) {
00811 LOG.debug("%s: %s: request to delete non-existant contact ignored",
00812 getName(), item.getKey());
00813 g_clear_error(&gerror);
00814 } else {
00815 throwError( string( "deleting contact " ) + item.getKey(),
00816 gerror );
00817 }
00818 }
00819 return status;
00820 }
00821
00822 const char *EvolutionContactSource::getMimeType() const
00823 {
00824 switch( m_vcardFormat ) {
00825 case EVC_FORMAT_VCARD_21:
00826 return "text/x-vcard";
00827 break;
00828 case EVC_FORMAT_VCARD_30:
00829 default:
00830 return "text/vcard";
00831 break;
00832 }
00833 }
00834
00835 const char *EvolutionContactSource::getMimeVersion() const
00836 {
00837 switch( m_vcardFormat ) {
00838 case EVC_FORMAT_VCARD_21:
00839 return "2.1";
00840 break;
00841 case EVC_FORMAT_VCARD_30:
00842 default:
00843 return "3.0";
00844 break;
00845 }
00846 }
00847
00848 void EvolutionContactSource::logItem(const string &uid, const string &info, bool debug)
00849 {
00850 if (LOG.getLevel() >= (debug ? LOG_LEVEL_DEBUG : LOG_LEVEL_INFO)) {
00851 string line;
00852 EContact *contact;
00853 GError *gerror = NULL;
00854
00855 if (e_book_get_contact( m_addressbook,
00856 uid.c_str(),
00857 &contact,
00858 &gerror )) {
00859 eptr<EContact, GObject> contactptr(contact);
00860
00861 const char *fileas = (const char *)e_contact_get_const( contact, E_CONTACT_FILE_AS );
00862 if (fileas) {
00863 line += fileas;
00864 } else {
00865 const char *name = (const char *)e_contact_get_const( contact, E_CONTACT_FULL_NAME );
00866 if (name) {
00867 line += name;
00868 } else {
00869 line += "<unnamed contact>";
00870 }
00871 }
00872 } else {
00873 g_clear_error(&gerror);
00874 line += "<name unavailable>";
00875 }
00876 line += " (";
00877 line += uid;
00878 line += "): ";
00879 line += info;
00880
00881 (LOG.*(debug ? &Log::debug : &Log::info))( "%s: %s", getName(), line.c_str() );
00882 }
00883 }
00884
00885 void EvolutionContactSource::logItem(const SyncItem &item, const string &info, bool debug)
00886 {
00887 if (LOG.getLevel() >= (debug ? LOG_LEVEL_DEBUG : LOG_LEVEL_INFO)) {
00888 string line;
00889 const char *data = (const char *)item.getData();
00890 int datasize = item.getDataSize();
00891 if (datasize <= 0) {
00892 data = "";
00893 datasize = 0;
00894 }
00895 string vcard( data, datasize );
00896
00897 size_t offset = vcard.find( "FN:");
00898 if (offset != vcard.npos) {
00899
00900
00901 int len = vcard.find_first_of("\r\n", offset) - offset - 3;
00902 line += vcard.substr( offset + 3, len );
00903 } else {
00904 line += "<unnamed contact>";
00905 }
00906
00907 if (!item.getKey() ) {
00908 line += ", NULL UID (?!)";
00909 } else if (!strlen( item.getKey() )) {
00910 line += ", empty UID";
00911 } else {
00912 line += ", ";
00913 line += item.getKey();
00914
00915 EContact *contact;
00916 GError *gerror = NULL;
00917 if (e_book_get_contact( m_addressbook,
00918 item.getKey(),
00919 &contact,
00920 &gerror )) {
00921 eptr<EContact, GObject> contactptr( contact, "contact" );
00922
00923 line += ", EV ";
00924 const char *fileas = (const char *)e_contact_get_const( contact, E_CONTACT_FILE_AS );
00925 if (fileas) {
00926 line += fileas;
00927 } else {
00928 const char *name = (const char *)e_contact_get_const( contact, E_CONTACT_FULL_NAME );
00929 if (name) {
00930 line += name;
00931 } else {
00932 line += "<unnamed contact>";
00933 }
00934 }
00935 } else {
00936 line += ", not in Evolution";
00937 }
00938 }
00939 line += ": ";
00940 line += info;
00941
00942 (LOG.*(debug ? &Log::debug : &Log::info))( "%s: %s", getName(), line.c_str() );
00943 }
00944 }
00945
00946 #endif
00947
00948 #ifdef ENABLE_MODULES
00949 # include "EvolutionContactSourceRegister.cpp"
00950 #endif