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
00027 #ifdef ENABLE_ADDRESSBOOK
00028
00029 #ifdef IPHONE
00030 # define ABAddRecord ABCAddRecord
00031 # define ABCopyArrayOfAllPeople ABCCopyArrayOfAllPeople
00032 # define ABGetSharedAddressBook ABCGetSharedAddressBook
00033 # define ABMultiValueAdd ABCMultiValueAdd
00034 # define ABMultiValueCopyLabelAtIndex ABCMultiValueCopyLabelAtIndex
00035 # define ABMultiValueCopyValueAtIndex ABCMultiValueCopyValueAtIndex
00036 # define ABMultiValueCount ABCMultiValueGetCount
00037 # define ABMultiValueCreateMutable ABCMultiValueCreateMutable
00038
00039 # define PersonCreateWrapper(_addressbook) ABCPersonCreateNewPerson(_addressbook)
00040
00041
00042
00043
00044
00045
00046
00047
00048 enum {
00049 IPHONE_PHOTO_SIZE_THUMBNAIL,
00050 IPHONE_PHOTO_SIZE_MEDIUM,
00051 IPHONE_PHOTO_SIZE_ORIGINAL
00052 };
00053 # define PersonSetImageDataWrapper(_person, _dataref) ABCPersonSetImageDataAndCropRect(_person, IPHONE_PHOTO_SIZE_THUMBNAIL, _dataref, 0,0,0,0)
00054 # define ABRecordCopyValue ABCRecordCopyValue
00055 # define ABRecordRemoveValue ABCRecordRemoveValue
00056 # define ABRecordSetValue ABCRecordSetValue
00057 # define ABRemoveRecord ABCRemoveRecord
00058 # define ABSave ABCSave
00059 # define kABAIMInstantProperty kABCAIMInstantProperty
00060 # define kABAddressCityKey kABCAddressCityKey
00061 # define kABAddressCountryKey kABCAddressCountryKey
00062 # define kABAddressHomeLabel kABCAddressHomeLabel
00063 # define kABAddressProperty kABCAddressProperty
00064 # define kABAddressStateKey kABCAddressStateKey
00065 # define kABAddressStreetKey kABCAddressStreetKey
00066 # define kABAddressWorkLabel kABCAddressWorkLabel
00067 # define kABAddressZIPKey kABCAddressZIPKey
00068 # define kABAssistantLabel kABCAssistantLabel
00069 # define kABBirthdayProperty kABCBirthdayProperty
00070 # define kABCreationDateProperty kABCCreationDateProperty
00071 # define kABDepartmentProperty kABCDepartmentProperty
00072 # define kABEmailHomeLabel kABCEmailHomeLabel
00073 # define kABEmailProperty kABCEmailProperty
00074 # define kABEmailWorkLabel kABCEmailWorkLabel
00075 # define kABFirstNameProperty kABCFirstNameProperty
00076 # define kABHomePageLabel kABCHomePageLabel
00077
00078 # define kABICQInstantProperty kABCICQInstantProperty
00079 # define kABJabberHomeLabel kABCJabberHomeLabel
00080 # define kABJabberInstantProperty kABCJabberInstantProperty
00081 # define kABJabberWorkLabel kABCJabberWorkLabel
00082 # define kABJobTitleProperty kABCJobTitleProperty
00083 # define kABLastNameProperty kABCLastNameProperty
00084 # define kABMSNInstantProperty kABCMSNInstantProperty
00085 # define kABManagerLabel kABCManagerLabel
00086 # define kABMiddleNameProperty kABCMiddleNameProperty
00087 # define kABModificationDateProperty kABCModificationDateProperty
00088 # define kABNicknameProperty kABCNicknameProperty
00089 # define kABNoteProperty kABCNoteProperty
00090 # define kABOrganizationProperty kABCOrganizationProperty
00091 # define kABOtherDatesProperty kABCOtherDatesProperty
00092 # define kABPhoneHomeFAXLabel kABCPhoneHomeFAXLabel
00093 # define kABPhoneHomeLabel kABCPhoneHomeLabel
00094 # define kABPhoneMainLabel kABCPhoneMainLabel
00095 # define kABPhoneMobileLabel kABCPhoneMobileLabel
00096 # define kABPhonePagerLabel kABCPhonePagerLabel
00097 # define kABPhoneProperty kABCPhoneProperty
00098 # define kABPhoneWorkFAXLabel kABCPhoneWorkFAXLabel
00099 # define kABPhoneWorkLabel kABCPhoneWorkLabel
00100 # define kABRelatedNamesProperty kABCRelatedNamesProperty
00101 # define kABSpouseLabel kABCSpouseLabel
00102 # define kABSuffixProperty kABCSuffixProperty
00103
00104
00105 # define kABYahooInstantProperty kABCYahooInstantProperty
00106 #else
00107 # define PersonCreateWrapper(_addressbook) ABPersonCreate()
00108 # define PersonSetImageDataWrapper(_person, _dataref) ABPersonSetImageData(_person, _dataref)
00109 #endif
00110 #include "EvolutionSyncClient.h"
00111 #include "AddressBookSource.h"
00112
00113 #include <common/base/Log.h>
00114 #include <common/base/util/StringBuffer.h>
00115 #include "vocl/VConverter.h"
00116
00117 #include <CoreFoundation/CoreFoundation.h>
00118
00119 using namespace vocl;
00120
00121
00122 static string CFString2Std(CFStringRef cfstring)
00123 {
00124 const char *str = CFStringGetCStringPtr(cfstring, kCFStringEncodingUTF8);
00125 if (str) {
00126 return string(str);
00127 }
00128
00129 CFIndex len = CFStringGetLength(cfstring) * 2 + 1;
00130 for (int tries = 0; tries < 3; tries++) {
00131 arrayptr<char> buf(new char[len], "buffer");
00132 if (CFStringGetCString(cfstring, buf, len, kCFStringEncodingUTF8)) {
00133 return string((char *)buf);
00134 }
00135 len *= 2;
00136 }
00137 EvolutionSyncClient::throwError("converting CF string failed");
00138 return "";
00139 }
00140
00141
00142 static CFStringRef Std2CFString(const string &str)
00143 {
00144 ref<CFStringRef> cfstring(CFStringCreateWithCString(NULL, str.c_str(), kCFStringEncodingUTF8), "conversion from CFString");
00145 return cfstring.release();
00146 }
00147
00148
00149 static const CFStringRef otherLabel(CFSTR("_$!<Other>!$_"));
00150
00151 static const CFStringRef workLabel(CFSTR("_$!<Work>!$_"));
00152
00153 static const CFStringRef mainWorkLabel(CFSTR("main work"));
00154
00155 #ifdef IPHONE
00156
00157
00158 extern "C" {
00159 extern const CFStringRef kABCHomePageProperty;
00160 extern const CFStringRef kABCURLProperty;
00161
00162 ABPersonRef ABCPersonCreateNewPerson(ABAddressBookRef addressbook);
00163
00164 ABRecordRef ABCPersonGetRecordForUniqueID(ABAddressBookRef addressBook, SInt32 uid);
00165 ABRecordRef ABCopyRecordForUniqueId(ABAddressBookRef addressBook, CFStringRef uniqueId) {
00166 SInt32 uid = CFStringGetIntValue(uniqueId);
00167 return ABCPersonGetRecordForUniqueID(addressBook, uid);
00168 }
00169
00170 SInt32 ABCRecordGetUniqueId(ABRecordRef record);
00171 CFStringRef ABRecordCopyUniqueId(ABRecordRef record) {
00172 SInt32 uid = ABCRecordGetUniqueId(record);
00173 return CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), uid);
00174 }
00175
00176 CFDataRef ABCPersonCopyImageData(ABPersonRef person, int format);
00177 bool ABCPersonSetImageData(ABPersonRef person, int format, CFDataRef data);
00178 bool ABCPersonSetImageDataAndCropRect(ABPersonRef person, int format, CFDataRef data, int crop_x, int crop_y, int crop_width, int crop_height);
00179 }
00180
00181 #endif
00182
00183
00184
00185
00186
00187
00188
00189
00190 static char *my_strtok_r(char *buffer, char delim, char **ptr, char **endptr)
00191 {
00192 char *res;
00193
00194 if (buffer) {
00195 *ptr = buffer;
00196 *endptr = buffer + strlen(buffer);
00197 }
00198 res = *ptr;
00199 if (res == *endptr) {
00200 return NULL;
00201 }
00202
00203 while (**ptr) {
00204 if (**ptr == delim) {
00205 **ptr = 0;
00206 (*ptr)++;
00207 break;
00208 }
00209 (*ptr)++;
00210 }
00211
00212 return res;
00213 }
00214
00215
00216 class vCard2ABPerson {
00217 public:
00218 vCard2ABPerson(string &vcard, ABPersonRef person) :
00219 m_vcard(vcard),
00220 m_person(person) {
00221 }
00222
00223
00224 void toPerson() {
00225 std::auto_ptr<VObject> vobj(VConverter::parse((char *)m_vcard.c_str()));
00226 if (vobj.get() == 0) {
00227 throwError("parsing contact");
00228 }
00229 vobj->toNativeEncoding();
00230
00231
00232
00233
00234 for (int mapindex = 0;
00235 m_mapping[mapindex].m_vCardProp;
00236 mapindex++) {
00237 const mapping &map = m_mapping[mapindex];
00238 if (map.m_abPersonProp) {
00239 if (!ABRecordRemoveValue(m_person, *map.m_abPersonProp)) {
00240 throwError("removing old value "
00241 #ifndef IPHONE
00242 + CFString2Std(*map.m_abPersonProp) + " " +
00243 #endif
00244 "failed");
00245 }
00246 }
00247 }
00248 for (int multi = 0; multi < MAX_MULTIVALUE; multi++) {
00249 if (!ABRecordRemoveValue(m_person, *m_multiProp[multi])) {
00250 throwError(string("removing old value ")
00251 #ifndef IPHONE
00252 + CFString2Std(*m_multiProp[multi]) + " "
00253 #endif
00254 + "failed");
00255 }
00256 }
00257
00258
00259 int propindex = 0;
00260 VProperty *vprop;
00261 while ((vprop = vobj->getProperty(propindex)) != NULL) {
00262 for (int mapindex = 0;
00263 m_mapping[mapindex].m_vCardProp;
00264 mapindex++) {
00265 const mapping &map = m_mapping[mapindex];
00266 if (!strcmp(map.m_vCardProp, vprop->getName())) {
00267 toPerson_t handler = map.m_toPerson;
00268 if (!handler) {
00269 handler = &vCard2ABPerson::toPersonString;
00270 }
00271 (this->*handler)(map, *vprop);
00272 break;
00273 }
00274 }
00275 propindex++;
00276 }
00277
00278
00279 for (int multi = 0; multi < MAX_MULTIVALUE; multi++) {
00280 if (m_multi[multi]) {
00281 setPersonProp(*m_multiProp[multi], m_multi[multi], false);
00282 }
00283 }
00284
00285 VProperty *photo = vobj->getProperty("PHOTO");
00286 if (photo) {
00287 int len;
00288 arrayptr<char> decoded((char *)b64_decode(len, photo->getValue()), "photo");
00289 ref<CFDataRef> data(CFDataCreate(NULL, (UInt8 *)(char *)decoded, len));
00290 if (!PersonSetImageDataWrapper(m_person, data)) {
00291 EvolutionSyncClient::throwError("cannot set photo data");
00292 }
00293 }
00294 }
00295
00296
00297 void fromPerson(bool asVCard30) {
00298 string tmp;
00299
00300
00301
00302
00303
00304
00305
00306
00307 m_vobj.addProperty("BEGIN", "VCARD");
00308 m_vobj.addProperty("VERSION", asVCard30 ? "3.0" : "2.1");
00309 m_vobj.setVersion(asVCard30 ? "3.0" : "2.1");
00310
00311
00312 for (int mapindex = 0;
00313 m_mapping[mapindex].m_vCardProp;
00314 mapindex++ ) {
00315 const mapping &map = m_mapping[mapindex];
00316 if (map.m_abPersonProp) {
00317 #ifdef IPHONE
00318
00319
00320
00321 CFTypeRef value = ABRecordCopyValue(m_person, *map.m_abPersonProp);
00322 #else
00323 ref<CFTypeRef> value(ABRecordCopyValue(m_person, *map.m_abPersonProp));
00324 #endif
00325 if (value) {
00326 fromPerson_t handler = map.m_fromPerson;
00327 if (!handler) {
00328 handler = &vCard2ABPerson::fromPersonString;
00329 }
00330 (this->*handler)(map, value);
00331 }
00332 }
00333 }
00334
00335
00336 string n;
00337 n += m_strings[LAST_NAME];
00338 n += VObject::SEMICOLON_REPLACEMENT;
00339 n += m_strings[FIRST_NAME];
00340 n += VObject::SEMICOLON_REPLACEMENT;
00341 n += m_strings[MIDDLE_NAME];
00342 n += VObject::SEMICOLON_REPLACEMENT;
00343 n += m_strings[TITLE];
00344 n += VObject::SEMICOLON_REPLACEMENT;
00345 n += m_strings[SUFFIX];
00346 m_vobj.addProperty("N", n.c_str());
00347
00348 if (m_strings[ORGANIZATION].size() ||
00349 m_strings[DEPARTMENT].size() ) {
00350 string org;
00351 org += m_strings[ORGANIZATION];
00352 org += VObject::SEMICOLON_REPLACEMENT;
00353 org += m_strings[DEPARTMENT];
00354 m_vobj.addProperty("ORG", org.c_str());
00355 }
00356
00357 ref<CFDataRef> photo;
00358 #ifdef IPHONE
00359
00360 for(int format = IPHONE_PHOTO_SIZE_ORIGINAL; format >= 0; format--) {
00361 photo.set(ABCPersonCopyImageData(m_person, format));
00362 if (photo) {
00363 break;
00364 }
00365 }
00366 #else
00367 photo.set(ABPersonCopyImageData(m_person));
00368 #endif
00369 if (photo) {
00370 StringBuffer encoded;
00371 b64_encode(encoded, (void *)CFDataGetBytePtr(photo), CFDataGetLength(photo));
00372 VProperty vprop("PHOTO");
00373 vprop.addParameter("ENCODING", asVCard30 ? "B" : "BASE64");
00374 vprop.setValue(encoded.c_str());
00375 m_vobj.addProperty(&vprop);
00376 }
00377
00378 m_vobj.addProperty("END", "VCARD");
00379 m_vobj.fromNativeEncoding();
00380 arrayptr<char> finalstr(m_vobj.toString(), "VOCL string");
00381 m_vcard = (char *)finalstr;
00382 }
00383
00384 private:
00385 string &m_vcard;
00386 ABPersonRef m_person;
00387 VObject m_vobj;
00388
00389 void throwError(const string &error) {
00390 EvolutionSyncClient::throwError(string("vCard<->Addressbook conversion: ") + error);
00391 }
00392
00393
00394 enum {
00395 FIRST_NAME,
00396 MIDDLE_NAME,
00397 LAST_NAME,
00398 TITLE,
00399 SUFFIX,
00400 ORGANIZATION,
00401 DEPARTMENT,
00402 MAX_STRINGS
00403 };
00404 string m_strings[MAX_STRINGS];
00405
00406
00407 enum {
00408 URLS,
00409 EMAILS,
00410 PHONES,
00411 #ifndef IPHONE
00412 DATES,
00413 AIM,
00414 JABBER,
00415 MSN,
00416 YAHOO,
00417 ICQ,
00418 #endif
00419 NAMES,
00420 ADDRESSES,
00421 MAX_MULTIVALUE
00422 };
00423 ref<ABMutableMultiValueRef, IPHONE_RELEASE> m_multi[MAX_MULTIVALUE];
00424
00425
00426
00427
00428
00429 static const CFStringRef *m_multiProp[MAX_MULTIVALUE];
00430
00431 struct mapping;
00432
00433 typedef void (vCard2ABPerson::*toPerson_t)(const mapping &map, VProperty &vprop);
00434
00435 typedef void (vCard2ABPerson::*fromPerson_t)(const mapping &map, CFTypeRef cftype);
00436
00437
00438 void setPersonProp(CFStringRef property, const string &str) {
00439 ref<CFStringRef> cfstring(Std2CFString(str));
00440 setPersonProp(property, cfstring);
00441 }
00442
00443 void setPersonProp(CFStringRef property, const char *str) {
00444 ref<CFStringRef> cfstring(Std2CFString(str));
00445 setPersonProp(property, cfstring);
00446 }
00447
00448
00449
00450
00451 void setPersonProp(CFStringRef property, CFTypeRef cftype, bool dump = true) {
00452 ref<CFStringRef> descr;
00453 if (dump) {
00454 descr.set(CFCopyDescription(cftype));
00455 }
00456 if (!ABRecordSetValue(m_person, property, cftype)) {
00457 if (dump) {
00458 throwError(string("setting ") +
00459 #ifndef IPHONE
00460 CFString2Std(property) +
00461 #else
00462 "property " +
00463 #endif
00464 + " to " + CFString2Std(descr) + "'");
00465 } else {
00466 throwError(string("setting ") +
00467 #ifndef IPHONE
00468 CFString2Std(property)
00469 #else
00470 "property"
00471 #endif
00472 );
00473 }
00474 }
00475 }
00476
00477
00478 void toPersonMultiVal(const mapping &map, CFStringRef label, CFTypeRef value) {
00479 if (!m_multi[map.m_customInt]) {
00480 m_multi[map.m_customInt].set(ABMultiValueCreateMutable(), "multivalue");
00481 }
00482 CFStringRef res;
00483 if (!ABMultiValueAdd(m_multi[map.m_customInt],
00484 value,
00485 label,
00486 &res)) {
00487 throwError(string("adding multi value for ") + map.m_vCardProp);
00488 } else {
00489 #ifndef IPHONE
00490 CFRelease(res);
00491 #endif
00492 }
00493 }
00494
00495
00496
00497
00498 static const struct mapping {
00499
00500 const char *m_vCardProp;
00501
00502 const CFStringRef *m_abPersonProp;
00503
00504 toPerson_t m_toPerson;
00505
00506 fromPerson_t m_fromPerson;
00507
00508 int m_customInt;
00509
00510 CFStringRef m_customString;
00511 } m_mapping[];
00512
00513
00514 void fromPersonString(const mapping &map, CFTypeRef cftype) {
00515 string value(CFString2Std((CFStringRef)cftype));
00516 m_vobj.addProperty(map.m_vCardProp, value.c_str());
00517 }
00518
00519
00520 void toPersonString(const mapping &map, VProperty &vprop) {
00521 const char *value = vprop.getValue();
00522
00523
00524
00525
00526
00527
00528
00529 if (value && *value) {
00530 setPersonProp(*map.m_abPersonProp, value);
00531 }
00532 }
00533
00534
00535 void fromPersonStoreString(const mapping &map, CFTypeRef cftype) {
00536 m_strings[map.m_customInt] = CFString2Std((CFStringRef)cftype);
00537 }
00538
00539
00540
00541
00542
00543 void toPersonStore(const mapping &map, VProperty &vprop) {
00544 const char *value = vprop.getValue();
00545 if (!value || !value[0]) {
00546 return;
00547 }
00548 ref<CFStringRef> cfstring(Std2CFString(value));
00549 CFStringRef label = map.m_customString;
00550 if (!label) {
00551
00552
00553 if (map.m_customString) {
00554 label = map.m_customString;
00555 } else if (vprop.isType("HOME")) {
00556 label = kABJabberHomeLabel;
00557 } else if (vprop.isType("WORK")) {
00558 label = kABJabberHomeLabel;
00559 } else {
00560 label = otherLabel;
00561 }
00562 }
00563
00564 toPersonMultiVal(map, label, cfstring);
00565 }
00566
00567
00568 void fromPersonDate(const mapping &map, CFTypeRef cftype) {
00569 ref<CFTimeZoneRef> tz(CFTimeZoneCopyDefault());
00570 CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(CFDateGetAbsoluteTime((CFDateRef)cftype), tz);
00571 char buffer[40];
00572 sprintf(buffer, "%04d-%02d-%02d", (int)date.year, date.month, date.day);
00573 m_vobj.addProperty(map.m_vCardProp, buffer);
00574 }
00575
00576
00577 void toPersonDate(const mapping &map, VProperty &vprop) {
00578 int year, month, day;
00579 const char *value = vprop.getValue();
00580 if (!value || !value[0]) {
00581 return;
00582 }
00583 if (sscanf(value, "%d-%d-%d", &year, &month, &day) == 3) {
00584 CFGregorianDate date;
00585 memset(&date, 0, sizeof(date));
00586 date.year = year;
00587 date.month = month;
00588 date.day = day;
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599
00600 ref<CFTimeZoneRef> tz(CFTimeZoneCopyDefault());
00601 ref<CFDateRef> cfdate(CFDateCreate(NULL, CFGregorianDateGetAbsoluteTime(date, tz)));
00602 if (cfdate) {
00603
00604 setPersonProp(*map.m_abPersonProp, cfdate);
00605 }
00606 }
00607 }
00608
00609
00610 void fromPersonURLs(const mapping &map, CFTypeRef cftype) {
00611 int index = ABMultiValueCount((ABMultiValueRef)cftype) - 1;
00612 while (index >= 0) {
00613 ref<CFStringRef> label((CFStringRef)ABMultiValueCopyLabelAtIndex((ABMultiValueRef)cftype, index), "label");
00614 ref<CFStringRef> value((CFStringRef)ABMultiValueCopyValueAtIndex((ABMultiValueRef)cftype, index), "value");
00615
00616 VProperty vprop("URL");
00617 string url = CFString2Std(value);
00618 vprop.setValue(url.c_str());
00619 if (CFStringCompare(label, (CFStringRef)kABHomePageLabel, 0) == kCFCompareEqualTo) {
00620
00621 } else if (CFStringCompare(label, (CFStringRef)workLabel, 0) == kCFCompareEqualTo) {
00622 vprop.addParameter("TYPE", "WORK");
00623 } else if (CFStringCompare(label, otherLabel, 0) == kCFCompareEqualTo) {
00624 vprop.addParameter("TYPE", "OTHER");
00625 } else {
00626 string labelstr = CFString2Std(label);
00627 vprop.addParameter("TYPE", labelstr.c_str());
00628 }
00629 m_vobj.addProperty(&vprop);
00630
00631 index--;
00632 }
00633 }
00634
00635
00636 void toPersonURLs(const mapping &map, VProperty &vprop) {
00637 const char *value = vprop.getValue();
00638 if (!value || !value[0]) {
00639 return;
00640 }
00641 arrayptr<char> buffer(wstrdup(value));
00642
00643 ref<CFStringRef> cfvalue(Std2CFString(value));
00644 CFStringRef label;
00645 ref<CFStringRef> custom;
00646 const char *type = vprop.getParameterValue("TYPE");
00647 if (vprop.isType("WORK")) {
00648 label = workLabel;
00649 } else if(vprop.isType("HOME")) {
00650 label = (CFStringRef)kABHomePageLabel;
00651 } else if(vprop.isType("OTHER")) {
00652 label = otherLabel;
00653 } else if (type) {
00654 custom.set(Std2CFString(type));
00655 label = custom;
00656 } else {
00657 label = (CFStringRef)kABHomePageLabel;
00658 }
00659 toPersonMultiVal(map, label, cfvalue);
00660 }
00661
00662
00663 void fromPersonEMail(const mapping &map, CFTypeRef cftype) {
00664 int index = ABMultiValueCount((ABMultiValueRef)cftype) - 1;
00665 while (index >= 0) {
00666 ref<CFStringRef> label((CFStringRef)ABMultiValueCopyLabelAtIndex((ABMultiValueRef)cftype, index), "label");
00667 ref<CFStringRef> value((CFStringRef)ABMultiValueCopyValueAtIndex((ABMultiValueRef)cftype, index), "value");
00668 VProperty vprop("EMAIL");
00669
00670 if (CFStringCompare(label, kABEmailWorkLabel, 0) == kCFCompareEqualTo) {
00671 vprop.addParameter("TYPE", "WORK");
00672 } else if (CFStringCompare(label, kABEmailHomeLabel, 0) == kCFCompareEqualTo) {
00673 vprop.addParameter("TYPE", "HOME");
00674 } else {
00675 string labelstr = CFString2Std(label);
00676 vprop.addParameter("TYPE", labelstr.c_str());
00677 }
00678
00679 string email = CFString2Std(value);
00680 vprop.setValue(email.c_str());
00681 m_vobj.addProperty(&vprop);
00682
00683 index--;
00684 }
00685 }
00686
00687
00688 void toPersonEMail(const mapping &map, VProperty &vprop) {
00689 const char *value = vprop.getValue();
00690 if (!value || !value[0]) {
00691 return;
00692 }
00693 arrayptr<char> buffer(wstrdup(value));
00694
00695 ref<CFStringRef> cfvalue(Std2CFString(value));
00696 CFStringRef label;
00697 ref<CFStringRef> custom;
00698 const char *type = vprop.getParameterValue("TYPE");
00699 if (vprop.isType("WORK")) {
00700 label = kABEmailWorkLabel;
00701 } else if(vprop.isType("HOME")) {
00702 label = kABEmailHomeLabel;
00703 } else if (type) {
00704 custom.set(Std2CFString(type));
00705 label = custom;
00706 } else {
00707 label = otherLabel;
00708 }
00709
00710 toPersonMultiVal(map, label, cfvalue);
00711 }
00712
00713
00714 void fromPersonAddr(const mapping &map, CFTypeRef cftype) {
00715 int index = ABMultiValueCount((ABMultiValueRef)cftype) - 1;
00716 while (index >= 0) {
00717 ref<CFStringRef> label((CFStringRef)ABMultiValueCopyLabelAtIndex((ABMultiValueRef)cftype, index), "label");
00718 ref<CFDictionaryRef> value((CFDictionaryRef)ABMultiValueCopyValueAtIndex((ABMultiValueRef)cftype, index), "value");
00719 CFStringRef part;
00720 VProperty vprop((char *)map.m_vCardProp);
00721
00722 string adr;
00723
00724 adr += VObject::SEMICOLON_REPLACEMENT;
00725
00726 adr += VObject::SEMICOLON_REPLACEMENT;
00727
00728 part = (CFStringRef)CFDictionaryGetValue(value, kABAddressStreetKey);
00729 if (part) {
00730 adr += CFString2Std(part);
00731 }
00732 adr += VObject::SEMICOLON_REPLACEMENT;
00733
00734 part = (CFStringRef)CFDictionaryGetValue(value, kABAddressCityKey);
00735 if (part) {
00736 adr += CFString2Std(part);
00737 }
00738 adr += VObject::SEMICOLON_REPLACEMENT;
00739
00740 part = (CFStringRef)CFDictionaryGetValue(value, kABAddressStateKey);
00741 if (part) {
00742 adr += CFString2Std(part);
00743 }
00744 adr += VObject::SEMICOLON_REPLACEMENT;
00745
00746 part = (CFStringRef)CFDictionaryGetValue(value, kABAddressZIPKey);
00747 if (part) {
00748 adr += CFString2Std(part);
00749 }
00750 adr += VObject::SEMICOLON_REPLACEMENT;
00751
00752 part = (CFStringRef)CFDictionaryGetValue(value, kABAddressCountryKey);
00753 if (part) {
00754 adr += CFString2Std(part);
00755 }
00756 adr += VObject::SEMICOLON_REPLACEMENT;
00757
00758
00759
00760 if (CFStringCompare(label, kABAddressWorkLabel, 0) == kCFCompareEqualTo) {
00761 vprop.addParameter("TYPE", "WORK");
00762 } else if (CFStringCompare(label, kABAddressHomeLabel, 0) == kCFCompareEqualTo) {
00763 vprop.addParameter("TYPE", "HOME");
00764 }
00765
00766 vprop.setValue(adr.c_str());
00767 m_vobj.addProperty(&vprop);
00768
00769 index--;
00770 }
00771 }
00772
00773
00774 void toPersonAddr(const mapping &map, VProperty &vprop) {
00775 const char *value = vprop.getValue();
00776 if (!value || !value[0]) {
00777 return;
00778 }
00779 arrayptr<char> buffer(wstrdup(value));
00780 char *saveptr, *endptr;
00781
00782 ref<CFMutableDictionaryRef> dict(CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
00783
00784
00785 my_strtok_r(buffer, VObject::SEMICOLON_REPLACEMENT, &saveptr, &endptr);
00786 my_strtok_r(NULL, VObject::SEMICOLON_REPLACEMENT, &saveptr, &endptr);
00787
00788 char *street = my_strtok_r(NULL, VObject::SEMICOLON_REPLACEMENT, &saveptr, &endptr);
00789 if (street && *street) {
00790 ref<CFStringRef> cfstring(Std2CFString(street));
00791 CFDictionarySetValue(dict, kABAddressStreetKey, cfstring);
00792 }
00793 char *city = my_strtok_r(NULL, VObject::SEMICOLON_REPLACEMENT, &saveptr, &endptr);
00794 if (city && *city) {
00795 ref<CFStringRef> cfstring(Std2CFString(city));
00796 CFDictionarySetValue(dict, kABAddressCityKey, cfstring);
00797 }
00798 char *region = my_strtok_r(NULL, VObject::SEMICOLON_REPLACEMENT, &saveptr, &endptr);
00799 if (region && *region) {
00800 ref<CFStringRef> cfstring(Std2CFString(region));
00801 CFDictionarySetValue(dict, kABAddressStateKey, cfstring);
00802 }
00803 char *zip = my_strtok_r(NULL, VObject::SEMICOLON_REPLACEMENT, &saveptr, &endptr);
00804 if (zip && *zip) {
00805 ref<CFStringRef> cfstring(Std2CFString(zip));
00806 CFDictionarySetValue(dict, kABAddressZIPKey, cfstring);
00807 }
00808 char *country = my_strtok_r(NULL, VObject::SEMICOLON_REPLACEMENT, &saveptr, &endptr);
00809 if (country && *country) {
00810 ref<CFStringRef> cfstring(Std2CFString(country));
00811 CFDictionarySetValue(dict, kABAddressCountryKey, cfstring);
00812 }
00813
00814 CFStringRef label;
00815 if (vprop.isType("WORK")) {
00816 label = kABAddressWorkLabel;
00817 } else if(vprop.isType("HOME")) {
00818 label = kABAddressHomeLabel;
00819 } else {
00820 label = otherLabel;
00821 }
00822
00823 toPersonMultiVal(map, label, dict);
00824 }
00825
00826
00827 void fromPersonPhone(const mapping &map, CFTypeRef cftype) {
00828 int index = ABMultiValueCount((ABMultiValueRef)cftype) - 1;
00829 while (index >= 0) {
00830 ref<CFStringRef> label((CFStringRef)ABMultiValueCopyLabelAtIndex((ABMultiValueRef)cftype, index), "label");
00831 ref<CFStringRef> value((CFStringRef)ABMultiValueCopyValueAtIndex((ABMultiValueRef)cftype, index), "value");
00832 VProperty vprop("TEL");
00833
00834 if (CFStringCompare(label, kABPhoneWorkLabel, 0) == kCFCompareEqualTo) {
00835 vprop.addParameter("TYPE", "WORK");
00836 vprop.addParameter("TYPE", "VOICE");
00837 } else if (CFStringCompare(label, mainWorkLabel, 0) == kCFCompareEqualTo) {
00838 vprop.addParameter("TYPE", "WORK");
00839 vprop.addParameter("TYPE", "PREF");
00840 } else if (CFStringCompare(label, kABPhoneHomeLabel, 0) == kCFCompareEqualTo) {
00841 vprop.addParameter("TYPE", "HOME");
00842 vprop.addParameter("TYPE", "VOICE");
00843 } else if (CFStringCompare(label, kABPhoneMobileLabel, 0) == kCFCompareEqualTo) {
00844 vprop.addParameter("TYPE", "CELL");
00845 } else if (CFStringCompare(label, kABPhoneMainLabel, 0) == kCFCompareEqualTo) {
00846 vprop.addParameter("TYPE", "PREF");
00847 vprop.addParameter("TYPE", "VOICE");
00848 } else if (CFStringCompare(label, kABPhoneHomeFAXLabel, 0) == kCFCompareEqualTo) {
00849 vprop.addParameter("TYPE", "HOME");
00850 vprop.addParameter("TYPE", "FAX");
00851 } else if (CFStringCompare(label, kABPhoneWorkFAXLabel, 0) == kCFCompareEqualTo) {
00852 vprop.addParameter("TYPE", "WORK");
00853 vprop.addParameter("TYPE", "FAX");
00854 } else if (CFStringCompare(label,kABPhonePagerLabel , 0) == kCFCompareEqualTo) {
00855 vprop.addParameter("TYPE", "PAGER");
00856 } else {
00857
00858 vprop.addParameter("TYPE", "VOICE");
00859 }
00860
00861 string phone = CFString2Std(value);
00862 vprop.setValue(phone.c_str());
00863 m_vobj.addProperty(&vprop);
00864
00865 index--;
00866 }
00867 }
00868
00869
00870 void toPersonPhone(const mapping &map, VProperty &vprop) {
00871 const char *value = vprop.getValue();
00872 if (!value || !value[0]) {
00873 return;
00874 }
00875 arrayptr<char> buffer(wstrdup(value));
00876
00877 ref<CFStringRef> cfvalue(Std2CFString(value));
00878 CFStringRef label;
00879 if (vprop.isType("WORK")) {
00880 if (vprop.isType("FAX")) {
00881 label = kABPhoneWorkFAXLabel;
00882 } else if (vprop.isType("PREF")) {
00883 label = mainWorkLabel;
00884 } else {
00885 label = kABPhoneWorkLabel;
00886 }
00887 } else if(vprop.isType("HOME")) {
00888 if (vprop.isType("FAX")) {
00889 label = kABPhoneHomeFAXLabel;
00890 } else {
00891 label = kABPhoneHomeLabel;
00892 }
00893 } else if(vprop.isType("PREF") || vprop.isType("VOICE")) {
00894 label = kABPhoneMainLabel;
00895 } else if(vprop.isType("PAGER")) {
00896 label = kABPhonePagerLabel;
00897 } else if(vprop.isType("CELL")) {
00898 label = kABPhoneMobileLabel;
00899 } else {
00900 label = otherLabel;
00901 }
00902
00903 toPersonMultiVal(map, label, cfvalue);
00904 }
00905
00906
00907
00908
00909
00910
00911 void fromPersonChat(const mapping &map, CFTypeRef cftype) {
00912 int index = ABMultiValueCount((ABMultiValueRef)cftype) - 1;
00913 while (index >= 0) {
00914 ref<CFStringRef> label((CFStringRef)ABMultiValueCopyLabelAtIndex((ABMultiValueRef)cftype, index), "label");
00915 ref<CFStringRef> value((CFStringRef)ABMultiValueCopyValueAtIndex((ABMultiValueRef)cftype, index), "value");
00916 VProperty vprop((char *)map.m_vCardProp);
00917
00918
00919
00920
00921 if (CFStringCompare(label, kABJabberWorkLabel, 0) == kCFCompareEqualTo) {
00922 vprop.addParameter("TYPE", "WORK");
00923 } else if (CFStringCompare(label, kABJabberHomeLabel, 0) == kCFCompareEqualTo) {
00924 vprop.addParameter("TYPE", "HOME");
00925 } else {
00926
00927 }
00928
00929 string im = CFString2Std(value);
00930 vprop.setValue(im.c_str());
00931 m_vobj.addProperty(&vprop);
00932
00933 index--;
00934 }
00935 }
00936
00937
00938 void fromPersonNames(const mapping &map, CFTypeRef cftype) {
00939 int index = ABMultiValueCount((ABMultiValueRef)cftype) - 1;
00940 while (index >= 0) {
00941 ref<CFStringRef> label((CFStringRef)ABMultiValueCopyLabelAtIndex((ABMultiValueRef)cftype, index), "label");
00942 ref<CFStringRef> value((CFStringRef)ABMultiValueCopyValueAtIndex((ABMultiValueRef)cftype, index), "value");
00943 string name = CFString2Std(value);
00944
00945
00946
00947
00948 if (CFStringCompare(label, kABManagerLabel, 0) == kCFCompareEqualTo) {
00949 m_vobj.addProperty("X-EVOLUTION-MANAGER", name.c_str());
00950 } else if (CFStringCompare(label, kABAssistantLabel, 0) == kCFCompareEqualTo) {
00951 m_vobj.addProperty("X-EVOLUTION-ASSISTANT", name.c_str());
00952 } else if (CFStringCompare(label, kABSpouseLabel, 0) == kCFCompareEqualTo) {
00953 m_vobj.addProperty("X-EVOLUTION-SPOUSE", name.c_str());
00954 } else {
00955
00956 }
00957
00958 index--;
00959 }
00960 }
00961
00962
00963
00964
00965
00966
00967 void toPersonName(const mapping &map, VProperty &vprop) {
00968 const char *value = vprop.getValue();
00969 if (!value || !value[0]) {
00970 return;
00971 }
00972 arrayptr<char> buffer(wstrdup(value));
00973 char *saveptr, *endptr;
00974
00975 char *last = my_strtok_r(buffer, VObject::SEMICOLON_REPLACEMENT, &saveptr, &endptr);
00976 if (last && *last) {
00977 setPersonProp(kABLastNameProperty, last);
00978 }
00979
00980 char *first = my_strtok_r(NULL, VObject::SEMICOLON_REPLACEMENT, &saveptr, &endptr);
00981 if (first && *first) {
00982 setPersonProp(kABFirstNameProperty, first);
00983 }
00984
00985 char *middle = my_strtok_r(NULL, VObject::SEMICOLON_REPLACEMENT, &saveptr, &endptr);
00986 if (middle && *middle) {
00987 setPersonProp(kABMiddleNameProperty, middle);
00988 }
00989
00990 char *prefix = my_strtok_r(NULL, VObject::SEMICOLON_REPLACEMENT, &saveptr, &endptr);
00991 #ifndef IPHONE
00992 if (prefix && *prefix) {
00993 setPersonProp(kABTitleProperty, prefix);
00994 }
00995 #endif
00996
00997 char *suffix = my_strtok_r(NULL, VObject::SEMICOLON_REPLACEMENT, &saveptr, &endptr);
00998 if (suffix && *suffix) {
00999 setPersonProp(kABSuffixProperty, suffix);
01000 }
01001 }
01002
01003
01004
01005
01006
01007
01008 void toPersonOrg(const mapping &map, VProperty &vprop) {
01009 const char *value = vprop.getValue();
01010 if (!value || !value[0]) {
01011 return;
01012 }
01013 arrayptr<char> buffer(wstrdup(value));
01014 char *saveptr, *endptr;
01015
01016 char *company = my_strtok_r(buffer, VObject::SEMICOLON_REPLACEMENT, &saveptr, &endptr);
01017 if (company && *company) {
01018 setPersonProp(kABOrganizationProperty, company);
01019 }
01020
01021 char *department = my_strtok_r(NULL, VObject::SEMICOLON_REPLACEMENT, &saveptr, &endptr);
01022 if (department && *department) {
01023 setPersonProp(kABDepartmentProperty, department);
01024 }
01025 }
01026 };
01027
01028 const CFStringRef *vCard2ABPerson::m_multiProp[MAX_MULTIVALUE] = {
01029 #ifdef IPHONE
01030 &kABCURLProperty,
01031 #else
01032 (CFStringRef*)&kABURLsProperty,
01033 #endif
01034 &kABEmailProperty,
01035 &kABPhoneProperty,
01036 #ifndef IPHONE
01037 &kABOtherDatesProperty,
01038 &kABAIMInstantProperty,
01039 &kABJabberInstantProperty,
01040 &kABMSNInstantProperty,
01041 &kABYahooInstantProperty,
01042 &kABICQInstantProperty,
01043 #endif
01044 &kABRelatedNamesProperty,
01045 &kABAddressProperty
01046 };
01047
01048 const vCard2ABPerson::mapping vCard2ABPerson::m_mapping[] = {
01049 { "", &kABFirstNameProperty, NULL, &vCard2ABPerson::fromPersonStoreString, FIRST_NAME },
01050 { "", &kABLastNameProperty, NULL, &vCard2ABPerson::fromPersonStoreString, LAST_NAME },
01051 { "", &kABMiddleNameProperty, NULL, &vCard2ABPerson::fromPersonStoreString, MIDDLE_NAME },
01052 #ifndef IPHONE
01053 { "", &kABTitleProperty, NULL, &vCard2ABPerson::fromPersonStoreString, TITLE },
01054 #endif
01055 { "", &kABSuffixProperty, NULL, &vCard2ABPerson::fromPersonStoreString, SUFFIX },
01056 { "N", 0, &vCard2ABPerson::toPersonName },
01057
01058
01059
01060
01061 { "BDAY", &kABBirthdayProperty, &vCard2ABPerson::toPersonDate, &vCard2ABPerson::fromPersonDate },
01062
01063 { "", &kABOrganizationProperty, NULL, &vCard2ABPerson::fromPersonStoreString, ORGANIZATION },
01064 { "", &kABDepartmentProperty, NULL, &vCard2ABPerson::fromPersonStoreString, DEPARTMENT },
01065 { "ORG", 0, &vCard2ABPerson::toPersonOrg },
01066
01067 { "TITLE", &kABJobTitleProperty },
01068
01069
01070 #ifdef IPHONE
01071 { "URL", &kABCURLProperty, &vCard2ABPerson::toPersonURLs, &vCard2ABPerson::fromPersonURLs, URLS },
01072 #else
01073
01074
01075
01076
01077 { "URL", (CFStringRef *)&kABHomePageProperty },
01078 { "", (CFStringRef *)&kABURLsProperty, NULL, &vCard2ABPerson::fromPersonURLs },
01079 #endif
01080 #if 0
01081 kABHomePageLabel
01082 #endif
01083
01084 { "EMAIL", &kABEmailProperty, &vCard2ABPerson::toPersonEMail, &vCard2ABPerson::fromPersonEMail, EMAILS },
01085 #if 0
01086 kABEmailWorkLabel
01087 kABEmailHomeLabel
01088 #endif
01089
01090 { "ADR", &kABAddressProperty, &vCard2ABPerson::toPersonAddr, &vCard2ABPerson::fromPersonAddr, ADDRESSES },
01091 #if 0
01092 kABAddressWorkLabel
01093 kABAddressHomeLabel
01094
01095 kABAddressStreetKey
01096 kABAddressCityKey
01097 kABAddressStateKey
01098 kABAddressZIPKey
01099 kABAddressCountryKey
01100 kABAddressCountryCodeKey
01101 #endif
01102
01103
01104 { "TEL", &kABPhoneProperty, &vCard2ABPerson::toPersonPhone, &vCard2ABPerson::fromPersonPhone, PHONES },
01105
01106 #if 0
01107 kABPhoneWorkLabel
01108 kABPhoneHomeLabel
01109 kABPhoneMobileLabel
01110 kABPhoneMainLabel
01111 kABPhoneHomeFAXLabel
01112 kABPhoneWorkFAXLabel
01113 kABPhonePagerLabel
01114 #endif
01115 #ifndef IPHONE
01116 { "X-AIM", &kABAIMInstantProperty, &vCard2ABPerson::toPersonStore, &vCard2ABPerson::fromPersonChat, AIM },
01117 { "X-JABBER", &kABJabberInstantProperty, &vCard2ABPerson::toPersonStore, &vCard2ABPerson::fromPersonChat, JABBER },
01118 { "X-MSN", &kABMSNInstantProperty, &vCard2ABPerson::toPersonStore, &vCard2ABPerson::fromPersonChat, MSN },
01119 { "X-YAHOO", &kABYahooInstantProperty, &vCard2ABPerson::toPersonStore, &vCard2ABPerson::fromPersonChat, YAHOO },
01120 { "X-ICQ", &kABICQInstantProperty, &vCard2ABPerson::toPersonStore, &vCard2ABPerson::fromPersonChat, ICQ },
01121 #endif
01122
01123 { "NOTE", &kABNoteProperty },
01124 { "NICKNAME", &kABNicknameProperty },
01125
01126
01127
01128 #ifndef IPHONE
01129 { "", &kABRelatedNamesProperty, NULL, &vCard2ABPerson::fromPersonNames },
01130 #endif
01131 #if 0
01132 kABMotherLabel
01133 kABFatherLabel
01134 kABParentLabel
01135 kABSisterLabel
01136 kABBrotherFAXLabel
01137 kABChildLabel
01138 kABFriendLabel
01139 kABSpouseLabel
01140 kABPartnerLabel
01141 kABAssistantLabel
01142 kABManagerLabel
01143 #endif
01144 { "X-EVOLUTION-MANAGER", 0, &vCard2ABPerson::toPersonStore, NULL, NAMES, kABManagerLabel },
01145 { "X-EVOLUTION-ASSISTANT", 0, &vCard2ABPerson::toPersonStore, NULL, NAMES, kABAssistantLabel },
01146 { "X-EVOLUTION-SPOUSE", 0, &vCard2ABPerson::toPersonStore, NULL, NAMES, kABSpouseLabel },
01147
01148
01149
01150
01151
01152
01153
01154
01155
01156
01157 { NULL }
01158 };
01159
01160
01161 string AddressBookSource::getModTime(ABRecordRef record)
01162 {
01163 double absolute;
01164 #ifdef IPHONE
01165 absolute = (double)(int)ABRecordCopyValue(record,
01166 kABModificationDateProperty);
01167 #else
01168 ref<CFDateRef> itemModTime((CFDateRef)ABRecordCopyValue(record,
01169 kABModificationDateProperty));
01170 if (!itemModTime) {
01171 itemModTime.set((CFDateRef)ABRecordCopyValue(record,
01172 kABCreationDateProperty));
01173 }
01174 if (!itemModTime) {
01175 throwError("extracting time stamp");
01176 }
01177 absolute = CFDateGetAbsoluteTime(itemModTime);
01178 #endif
01179
01180
01181
01182
01183
01184 char buffer[128];
01185 sprintf(buffer, "%.0f", ceil(absolute));
01186 return buffer;
01187 }
01188
01189
01190 AddressBookSource::AddressBookSource(const EvolutionSyncSourceParams ¶ms, bool asVCard30) :
01191 TrackingSyncSource(params),
01192 m_addressbook(0),
01193 m_asVCard30(asVCard30)
01194 {
01195 }
01196
01197 EvolutionSyncSource::Databases AddressBookSource::getDatabases()
01198 {
01199 Databases result;
01200
01201 result.push_back(Database("<<system>>", ""));
01202 return result;
01203 }
01204
01205 void AddressBookSource::open()
01206 {
01207 m_addressbook = ABGetSharedAddressBook();
01208 if (!m_addressbook) {
01209 throwError("opening address book");
01210 }
01211 }
01212
01213 void AddressBookSource::listAllItems(RevisionMap_t &revisions)
01214 {
01215 ref<CFArrayRef> allPersons(ABCopyArrayOfAllPeople(m_addressbook), "list of all people");
01216
01217 for (CFIndex i = 0; i < CFArrayGetCount(allPersons); i++) {
01218 ref<CFStringRef> cfuid(ABRecordCopyUniqueId((ABRecordRef)CFArrayGetValueAtIndex(allPersons, i)), "reading UID");
01219 string uid(CFString2Std(cfuid));
01220
01221 revisions[uid] = getModTime((ABRecordRef)CFArrayGetValueAtIndex(allPersons, i));
01222 }
01223 }
01224
01225 void AddressBookSource::close()
01226 {
01227 if (m_addressbook && !hasFailed()) {
01228 LOG.debug("flushing address book");
01229
01230 if (!ABSave(m_addressbook)) {
01231 throwError("saving address book");
01232 }
01233
01234
01235
01236
01237 sleep(2);
01238
01239 LOG.debug("done with address book");
01240 }
01241
01242 m_addressbook = NULL;
01243 }
01244
01245 void AddressBookSource::exportData(ostream &out)
01246 {
01247 ref<CFArrayRef> allPersons(ABCopyArrayOfAllPeople(m_addressbook), "list of all people");
01248
01249 for (CFIndex i = 0; i < CFArrayGetCount(allPersons); i++) {
01250 ABRecordRef person = (ABRecordRef)CFArrayGetValueAtIndex(allPersons, i);
01251
01252 ref<CFStringRef> cfuid(ABRecordCopyUniqueId(person), "reading UID");
01253 string uid(CFString2Std(cfuid));
01254 cxxptr<SyncItem> item(createItem(uid, true), "sync item");
01255
01256 out << (char *)item->getData() << "\n";
01257 }
01258 }
01259
01260 SyncItem *AddressBookSource::createItem(const string &uid, bool asVCard30)
01261 {
01262 logItem(uid, "extracting from address book", true);
01263
01264 ref<CFStringRef> cfuid(Std2CFString(uid));
01265 ref<ABPersonRef> person((ABPersonRef)ABCopyRecordForUniqueId(m_addressbook, cfuid), "contact");
01266 auto_ptr<SyncItem> item(new SyncItem(uid.c_str()));
01267
01268 #ifdef USE_ADDRESS_BOOK_VCARD
01269 ref<CFDataRef> vcard(ABPersonCopyVCardRepresentation(person), "vcard");
01270 LOG.debug("%*s", (int)CFDataGetLength(vcard), (const char *)CFDataGetBytePtr(vcard));
01271 item->setData(CFDataGetBytePtr(vcard), CFDataGetLength(vcard));
01272 #else
01273 string vcard;
01274 try {
01275 vCard2ABPerson conv(vcard, person);
01276 conv.fromPerson(asVCard30);
01277 } catch (const std::exception &ex) {
01278 throwError("creating vCard for " + uid + " failed: " + ex.what());
01279 }
01280 item->setData(vcard.c_str(), vcard.size());
01281 #endif
01282
01283 item->setDataType(getMimeType());
01284 item->setModificationTime(0);
01285
01286 return item.release();
01287 }
01288
01289 AddressBookSource::InsertItemResult AddressBookSource::insertItem(const string &luid, const SyncItem &item)
01290 {
01291 bool update = !luid.empty();
01292 string newluid = luid;
01293 string data = (const char *)item.getData();
01294 ref<ABPersonRef> person;
01295
01296 #ifdef USE_ADDRESS_BOOK_VCARD
01297 if (uid) {
01298
01299 deleteItem(uid);
01300 }
01301
01302 ref<CFDataRef> vcard(CFDataCreate(NULL, (const UInt8 *)data.c_str(), data.size()), "vcard");
01303 person.set((ABPersonRef)ABPersonCreateWithVCardRepresentation(vcard));
01304 if (!person) {
01305 throwError(string("parsing vcard ") + data);
01306 }
01307 #else
01308 if (update) {
01309
01310 ref<CFStringRef> cfuid(Std2CFString(luid));
01311 person.set((ABPersonRef)ABCopyRecordForUniqueId(m_addressbook, cfuid), "contact");
01312 } else {
01313
01314 person.set(PersonCreateWrapper(m_addressbook), "contact");
01315 }
01316 try {
01317 LOG.debug("storing vCard for %s:\n%s",
01318 update ? luid.c_str() : "new contact",
01319 data.c_str());
01320 vCard2ABPerson converter(data, person);
01321 converter.toPerson();
01322 } catch (const std::exception &ex) {
01323 throwError(string("storing vCard for ") + (update ? luid : "new contact") + " failed: " + ex.what());
01324 }
01325 #endif
01326
01327
01328
01329
01330 CFAbsoluteTime nowabs = CFAbsoluteTimeGetCurrent();
01331 #ifdef IPHONE
01332 void *now = (void *)(int)round(nowabs);
01333 #else
01334 ref<CFDateRef> now(CFDateCreate(NULL, nowabs), "current time");
01335 #endif
01336 if (!ABRecordSetValue(person, kABModificationDateProperty, now)) {
01337 throwError("setting mod time");
01338 }
01339
01340
01341 if (update || ABAddRecord(m_addressbook, person)) {
01342
01343 ABSave(m_addressbook);
01344
01345 ref<CFStringRef> cfuid(ABRecordCopyUniqueId(person), "uid");
01346 newluid = CFString2Std(cfuid);
01347 } else {
01348 throwError("storing new contact");
01349 }
01350 string modtime = getModTime(person);
01351
01352 return InsertItemResult(newluid, modtime, false);
01353 }
01354
01355 void AddressBookSource::deleteItem(const string &uid)
01356 {
01357 ref<CFStringRef> cfuid(Std2CFString(uid.c_str()));
01358 ref<ABPersonRef> person((ABPersonRef)ABCopyRecordForUniqueId(m_addressbook, cfuid));
01359
01360 if (person) {
01361 if (!ABRemoveRecord(m_addressbook, person)) {
01362 throwError(string("deleting contact ") + uid);
01363 }
01364 } else {
01365 LOG.debug("%s: %s: request to delete non-existant contact ignored",
01366 getName(), uid.c_str());
01367 }
01368 }
01369
01370 void AddressBookSource::logItem(const string &uid, const string &info, bool debug)
01371 {
01372 if (LOG.getLevel() >= (debug ? LOG_LEVEL_DEBUG : LOG_LEVEL_INFO)) {
01373 string line;
01374
01375 #if 0
01376
01377
01378 if (e_book_get_contact( m_addressbook,
01379 uid.c_str(),
01380 &contact,
01381 &gerror )) {
01382 const char *fileas = (const char *)e_contact_get_const( contact, E_CONTACT_FILE_AS );
01383 if (fileas) {
01384 line += fileas;
01385 } else {
01386 const char *name = (const char *)e_contact_get_const( contact, E_CONTACT_FULL_NAME );
01387 if (name) {
01388 line += name;
01389 } else {
01390 line += "<unnamed contact>";
01391 }
01392 }
01393 } else {
01394 line += "<name unavailable>";
01395 }
01396 #endif
01397
01398 line += " (";
01399 line += uid;
01400 line += "): ";
01401 line += info;
01402
01403 (LOG.*(debug ? &Log::debug : &Log::info))( "%s: %s", getName(), line.c_str() );
01404 }
01405 }
01406
01407 void AddressBookSource::logItem(const SyncItem &item, const string &info, bool debug)
01408 {
01409 if (LOG.getLevel() >= (debug ? LOG_LEVEL_DEBUG : LOG_LEVEL_INFO)) {
01410 string line;
01411 const char *data = (const char *)item.getData();
01412 int datasize = item.getDataSize();
01413 if (datasize <= 0) {
01414 data = "";
01415 datasize = 0;
01416 }
01417 string vcard( data, datasize );
01418
01419 size_t offset = vcard.find( "FN:");
01420 if (offset != vcard.npos) {
01421 int len = vcard.find( "\r", offset ) - offset - 3;
01422 line += vcard.substr( offset + 3, len );
01423 } else {
01424 line += "<unnamed contact>";
01425 }
01426
01427 if (!item.getKey() ) {
01428 line += ", NULL UID (?!)";
01429 } else if (!strlen( item.getKey() )) {
01430 line += ", empty UID";
01431 } else {
01432 line += ", ";
01433 line += item.getKey();
01434
01435 #if 0
01436
01437 EContact *contact;
01438 GError *gerror = NULL;
01439 if (e_book_get_contact( m_addressbook,
01440 item.getKey(),
01441 &contact,
01442 &gerror )) {
01443 line += ", EV ";
01444 const char *fileas = (const char *)e_contact_get_const( contact, E_CONTACT_FILE_AS );
01445 if (fileas) {
01446 line += fileas;
01447 } else {
01448 const char *name = (const char *)e_contact_get_const( contact, E_CONTACT_FULL_NAME );
01449 if (name) {
01450 line += name;
01451 } else {
01452 line += "<unnamed contact>";
01453 }
01454 }
01455 } else {
01456 line += ", not in Evolution";
01457 }
01458 #endif
01459 }
01460 line += ": ";
01461 line += info;
01462
01463 (LOG.*(debug ? &Log::debug : &Log::info))( "%s: %s", getName(), line.c_str() );
01464 }
01465 }
01466
01467 #endif