00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "r_contact.h"
00023 #include "record-internal.h"
00024 #include "protocol.h"
00025 #include "protostructs.h"
00026 #include "data.h"
00027 #include "time.h"
00028 #include "error.h"
00029 #include "endian.h"
00030 #include "iconv.h"
00031 #include <ostream>
00032 #include <iomanip>
00033 #include <time.h>
00034 #include <stdexcept>
00035
00036 #define __DEBUG_MODE__
00037 #include "debug.h"
00038
00039 using namespace std;
00040 using namespace Barry::Protocol;
00041
00042 namespace Barry {
00043
00044
00045
00046
00047
00048
00049
00050 #define CFC_EMAIL 1
00051 #define CFC_PHONE 2
00052 #define CFC_FAX 3
00053 #define CFC_WORK_PHONE 6
00054 #define CFC_HOME_PHONE 7
00055 #define CFC_MOBILE_PHONE 8
00056 #define CFC_PAGER 9
00057 #define CFC_PIN 10
00058 #define CFC_RADIO 14 // 0x0e
00059 #define CFC_WORK_PHONE_2 16 // 0x10
00060 #define CFC_HOME_PHONE_2 17 // 0x11
00061 #define CFC_OTHER_PHONE 18 // 0x12
00062 #define CFC_MOBILE_PHONE_2 19 // 0x13
00063 #define CFC_HOME_FAX 20 // 0x14
00064 #define CFC_NAME 32 // 0x20 used twice, in first/last name order
00065 #define CFC_COMPANY 33
00066 #define CFC_DEFAULT_COMM_METHOD 34
00067 #define CFC_ADDRESS1 35
00068 #define CFC_ADDRESS2 36
00069 #define CFC_ADDRESS3 37
00070 #define CFC_CITY 38
00071 #define CFC_PROVINCE 39
00072 #define CFC_POSTAL_CODE 40
00073 #define CFC_COUNTRY 41
00074 #define CFC_TITLE 42 // 0x2a
00075 #define CFC_PUBLIC_KEY 43
00076 #define CFC_GROUP_FLAG 44
00077 #define CFC_GROUP_LINK 52
00078 #define CFC_URL 54 // 0x36
00079 #define CFC_PREFIX 55 // 0x37
00080 #define CFC_CATEGORY 59 // 0x3B
00081 #define CFC_HOME_ADDRESS1 61 // 0x3D
00082 #define CFC_HOME_ADDRESS2 62 // 0x3E
00083
00084
00085 #define CFC_HOME_ADDRESS3 63 // 0x3F
00086 #define CFC_NOTES 64 // 0x40
00087 #define CFC_USER_DEFINED_1 65 // 0x41
00088 #define CFC_USER_DEFINED_2 66 // 0x42
00089 #define CFC_USER_DEFINED_3 67 // 0x43
00090 #define CFC_USER_DEFINED_4 68 // 0x44
00091 #define CFC_HOME_CITY 69 // 0x45
00092 #define CFC_HOME_PROVINCE 70 // 0x46
00093 #define CFC_HOME_POSTAL_CODE 71 // 0x47
00094 #define CFC_HOME_COUNTRY 72 // 0x48
00095 #define CFC_IMAGE 77 // 0x4D
00096 #define CFC_BIRTHDAY 82 // 0x52
00097 #define CFC_ANNIVERSARY 83 // 0x53
00098 #define CFC_MAYBE_CATEGORYID 84 // 0x54
00099 #define CFC_UNIQUEID 85 // 0x55
00100 #define CFC_NICKNAME 86 // 0x56
00101 #define CFC_INVALID_FIELD 255
00102
00103
00104 static FieldLink<Contact> ContactFieldLinks[] = {
00105 { CFC_NICKNAME, "Nickname", 0,0, &Contact::Nickname, 0, 0, 0, 0, true },
00106 { CFC_PHONE, "Phone", 0,0, &Contact::Phone, 0, 0, 0, 0, true },
00107 { CFC_FAX, "Fax", "facsimileTelephoneNumber",0, &Contact::Fax, 0, 0, 0, 0, true },
00108 { CFC_HOME_FAX, "HomeFax", 0,0, &Contact::HomeFax, 0, 0, 0, 0, true },
00109 { CFC_WORK_PHONE, "WorkPhone", "telephoneNumber",0, &Contact::WorkPhone, 0, 0, 0, 0, true },
00110 { CFC_HOME_PHONE, "HomePhone", "homePhone",0, &Contact::HomePhone, 0, 0, 0, 0, true },
00111 { CFC_MOBILE_PHONE, "MobilePhone","mobile",0, &Contact::MobilePhone, 0, 0, 0, 0, true },
00112 { CFC_MOBILE_PHONE_2,"MobilePhone2",0,0, &Contact::MobilePhone2, 0, 0, 0, 0, true },
00113 { CFC_PAGER, "Pager", "pager",0, &Contact::Pager, 0, 0, 0, 0, true },
00114 { CFC_PIN, "PIN", 0,0, &Contact::PIN, 0, 0, 0, 0, true },
00115 { CFC_RADIO, "Radio", 0,0, &Contact::Radio, 0, 0, 0, 0, true },
00116 { CFC_WORK_PHONE_2, "WorkPhone2", 0,0, &Contact::WorkPhone2, 0, 0, 0, 0, true },
00117 { CFC_HOME_PHONE_2, "HomePhone2", 0,0, &Contact::HomePhone2, 0, 0, 0, 0, true },
00118 { CFC_OTHER_PHONE, "OtherPhone", 0,0, &Contact::OtherPhone, 0, 0, 0, 0, true },
00119 { CFC_COMPANY, "Company", "o",0, &Contact::Company, 0, 0, 0, 0, true },
00120 { CFC_DEFAULT_COMM_METHOD,"DefaultCommMethod",0,0, &Contact::DefaultCommunicationsMethod, 0, 0, 0, 0, true },
00121 { CFC_ADDRESS1, "WorkAddress1", 0,0, 0, 0, 0, &Contact::WorkAddress, &PostalAddress::Address1, true },
00122 { CFC_ADDRESS2, "WorkAddress2", 0,0, 0, 0, 0, &Contact::WorkAddress, &PostalAddress::Address2, true },
00123 { CFC_ADDRESS3, "WorkAddress3", 0,0, 0, 0, 0, &Contact::WorkAddress, &PostalAddress::Address3, true },
00124 { CFC_CITY, "WorkCity", "l",0, 0, 0, 0, &Contact::WorkAddress, &PostalAddress::City, true },
00125 { CFC_PROVINCE, "WorkProvince", "st",0, 0, 0, 0, &Contact::WorkAddress, &PostalAddress::Province, true },
00126 { CFC_POSTAL_CODE, "WorkPostalCode", "postalCode",0, 0, 0, 0, &Contact::WorkAddress, &PostalAddress::PostalCode, true },
00127 { CFC_COUNTRY, "WorkCountry", "c", "country", 0, 0, 0, &Contact::WorkAddress, &PostalAddress::Country, true },
00128 { CFC_TITLE, "JobTitle", "title",0, &Contact::JobTitle, 0, 0, 0, 0, true },
00129 { CFC_PUBLIC_KEY, "PublicKey", 0,0, &Contact::PublicKey, 0, 0, 0, 0, false },
00130 { CFC_URL, "URL", 0,0, &Contact::URL, 0, 0, 0, 0, true },
00131 { CFC_PREFIX, "Prefix", 0,0, &Contact::Prefix, 0, 0, 0, 0, true },
00132 { CFC_HOME_ADDRESS1,"HomeAddress1", 0,0, 0, 0, 0, &Contact::HomeAddress, &PostalAddress::Address1, true },
00133 { CFC_HOME_ADDRESS2,"HomeAddress2", 0,0, 0, 0, 0, &Contact::HomeAddress, &PostalAddress::Address2, true },
00134 { CFC_HOME_ADDRESS3,"HomeAddress3", 0,0, 0, 0, 0, &Contact::HomeAddress, &PostalAddress::Address3, true },
00135 { CFC_NOTES, "Notes", 0,0, &Contact::Notes, 0, 0, 0, 0, true },
00136 { CFC_USER_DEFINED_1, "UserDefined1", 0,0, &Contact::UserDefined1, 0, 0, 0, 0, true },
00137 { CFC_USER_DEFINED_2, "UserDefined2", 0,0, &Contact::UserDefined2, 0, 0, 0, 0, true },
00138 { CFC_USER_DEFINED_3, "UserDefined3", 0,0, &Contact::UserDefined3, 0, 0, 0, 0, true },
00139 { CFC_USER_DEFINED_4, "UserDefined4", 0,0, &Contact::UserDefined4, 0, 0, 0, 0, true },
00140 { CFC_HOME_CITY, "HomeCity", 0,0, 0, 0, 0, &Contact::HomeAddress, &PostalAddress::City, true },
00141 { CFC_HOME_PROVINCE,"HomeProvince", 0,0, 0, 0, 0, &Contact::HomeAddress, &PostalAddress::Province, true },
00142 { CFC_HOME_POSTAL_CODE, "HomePostalCode", 0,0, 0, 0, 0, &Contact::HomeAddress, &PostalAddress::PostalCode, true },
00143 { CFC_HOME_COUNTRY, "HomeCountry",0,0, 0, 0, 0, &Contact::HomeAddress, &PostalAddress::Country, true },
00144 { CFC_IMAGE, "Image", 0,0, &Contact::Image, 0, 0, 0, 0, false },
00145 { CFC_INVALID_FIELD,"EndOfList", 0, 0, 0, 0, 0, 0, 0, false }
00146 };
00147
00148 Contact::Contact()
00149 : RecType(Contact::GetDefaultRecType()),
00150 RecordId(0),
00151 m_FirstNameSeen(false)
00152 {
00153 }
00154
00155 Contact::~Contact()
00156 {
00157 }
00158
00159 const unsigned char* Contact::ParseField(const unsigned char *begin,
00160 const unsigned char *end,
00161 const IConverter *ic)
00162 {
00163 const CommonField *field = (const CommonField *) begin;
00164
00165
00166 begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
00167 if( begin > end )
00168 return begin;
00169
00170 if( !btohs(field->size) )
00171 return begin;
00172
00173
00174 for( FieldLink<Contact> *b = ContactFieldLinks;
00175 b->type != CFC_INVALID_FIELD;
00176 b++ )
00177 {
00178 if( b->type == field->type ) {
00179 if( b->strMember ) {
00180 std::string &s = this->*(b->strMember);
00181 s = ParseFieldString(field);
00182 if( b->iconvNeeded && ic )
00183 s = ic->FromBB(s);
00184 return begin;
00185 }
00186 else if( b->postMember && b->postField ) {
00187 std::string &s = (this->*(b->postMember)).*(b->postField);
00188 s = ParseFieldString(field);
00189 if( b->iconvNeeded && ic )
00190 s = ic->FromBB(s);
00191 return begin;
00192 }
00193 else {
00194 break;
00195 }
00196 }
00197 }
00198
00199
00200 switch( field->type )
00201 {
00202 case CFC_EMAIL: {
00203 std::string s = ParseFieldString(field);
00204 if( ic )
00205 s = ic->FromBB(s);
00206 EmailAddresses.push_back( s );
00207 }
00208 return begin;
00209
00210 case CFC_NAME: {
00211
00212 std::string *name;
00213 if( FirstName.size() || m_FirstNameSeen ) {
00214
00215 name = &LastName;
00216 m_FirstNameSeen = false;
00217 }
00218 else {
00219 name = &FirstName;
00220 m_FirstNameSeen = true;
00221 }
00222
00223 *name = ParseFieldString(field);
00224 if( ic )
00225 *name = ic->FromBB(*name);
00226 }
00227 return begin;
00228
00229 case CFC_GROUP_LINK:
00230
00231 GroupLinks.push_back(
00232 GroupLink(field->u.link.uniqueId,
00233 field->u.link.unknown));
00234 return begin;
00235
00236 case CFC_GROUP_FLAG:
00237
00238
00239 return begin;
00240
00241 case CFC_CATEGORY: {
00242 std::string catstring = ParseFieldString(field);
00243 if( ic )
00244 catstring = ic->FromBB(catstring);
00245 Categories.CategoryStr2List(catstring);
00246 }
00247 return begin;
00248
00249 case CFC_BIRTHDAY: {
00250 std::string bstring = ParseFieldString(field);
00251 Birthday.FromBBString(bstring);
00252 }
00253 return begin;
00254
00255 case CFC_ANNIVERSARY: {
00256 std::string astring = ParseFieldString(field);
00257 Anniversary.FromBBString(astring);
00258 }
00259 return begin;
00260
00261 case CFC_UNIQUEID:
00262
00263
00264
00265
00266 return begin;
00267 }
00268
00269
00270 UnknownField uf;
00271 uf.type = field->type;
00272 uf.data.assign((const char*)field->u.raw, btohs(field->size));
00273 Unknowns.push_back(uf);
00274
00275
00276 return begin;
00277 }
00278
00279 void Contact::ParseHeader(const Data &data, size_t &offset)
00280 {
00281
00282 }
00283
00284 void Contact::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
00285 {
00286 const unsigned char *finish = ParseCommonFields(*this,
00287 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
00288 offset += finish - (data.GetData() + offset);
00289 }
00290
00291 void Contact::BuildHeader(Data &data, size_t &offset) const
00292 {
00293
00294 }
00295
00296
00297
00298
00299
00300
00301 void Contact::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
00302 {
00303 data.Zap();
00304
00305
00306
00307 if( !GetFullName().size() && !Company.size() )
00308 throw BadData("Contact must have name or company name.");
00309
00310
00311
00312 if( GroupLinks.size() )
00313 BuildField(data, offset, CFC_GROUP_FLAG, 'G');
00314
00315
00316 if( FirstName.size() ) {
00317 std::string s = ic ? ic->ToBB(FirstName) : FirstName;
00318 BuildField(data, offset, CFC_NAME, s);
00319 }
00320 if( LastName.size() ) {
00321 if( !FirstName.size() ) {
00322
00323
00324
00325 BuildField(data, offset, CFC_NAME, "");
00326 }
00327 BuildField(data, offset, CFC_NAME, ic ? ic->ToBB(LastName) : LastName);
00328 }
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340 BuildField(data, offset, CFC_UNIQUEID, RecordId);
00341
00342
00343 EmailList::const_iterator eai = EmailAddresses.begin();
00344 for( ; eai != EmailAddresses.end(); ++eai ) {
00345 if( eai->size() ) {
00346 BuildField(data, offset, CFC_EMAIL, ic ? ic->ToBB(*eai) : *eai);
00347 }
00348 }
00349
00350
00351 for( FieldLink<Contact> *b = ContactFieldLinks;
00352 b->type != CFC_INVALID_FIELD;
00353 b++ )
00354 {
00355
00356 if( b->strMember ) {
00357 const std::string &field = this->*(b->strMember);
00358 if( field.size() ) {
00359 std::string s = (b->iconvNeeded && ic) ? ic->ToBB(field) : field;
00360 BuildField(data, offset, b->type, s);
00361 }
00362 }
00363 else if( b->postMember && b->postField ) {
00364 const std::string &field = (this->*(b->postMember)).*(b->postField);
00365 if( field.size() ) {
00366 std::string s = (b->iconvNeeded && ic) ? ic->ToBB(field) : field;
00367 BuildField(data, offset, b->type, s);
00368 }
00369 }
00370 }
00371
00372
00373 GroupLinksType::const_iterator
00374 gb = GroupLinks.begin(), ge = GroupLinks.end();
00375 for( ; gb != ge; gb++ ) {
00376 Barry::Protocol::GroupLink link;
00377 link.uniqueId = htobl(gb->Link);
00378 link.unknown = htobs(gb->Unknown);
00379 BuildField(data, offset, CFC_GROUP_LINK, link);
00380 }
00381
00382
00383 if( Categories.size() ) {
00384 string store;
00385 Categories.CategoryList2Str(store);
00386 BuildField(data, offset, CFC_CATEGORY, ic ? ic->ToBB(store) : store);
00387 }
00388
00389
00390 if( Birthday.HasData() )
00391 BuildField(data, offset, CFC_BIRTHDAY, Birthday.ToBBString());
00392 if( Anniversary.HasData() )
00393 BuildField(data, offset, CFC_ANNIVERSARY, Anniversary.ToBBString());
00394
00395
00396 UnknownsType::const_iterator
00397 ub = Unknowns.begin(), ue = Unknowns.end();
00398 for( ; ub != ue; ub++ ) {
00399 BuildField(data, offset, *ub);
00400 }
00401
00402 data.ReleaseBuffer(offset);
00403 }
00404
00405 void Contact::Clear()
00406 {
00407 RecType = GetDefaultRecType();
00408 RecordId = 0;
00409
00410 EmailAddresses.clear();
00411 Phone.clear();
00412
00413 Fax.clear();
00414 HomeFax.clear();
00415 WorkPhone.clear();
00416 HomePhone.clear();
00417 MobilePhone.clear();
00418 MobilePhone2.clear();
00419 Pager.clear();
00420 PIN.clear();
00421 Radio.clear();
00422 WorkPhone2.clear();
00423 HomePhone2.clear();
00424 OtherPhone.clear();
00425 FirstName.clear();
00426 LastName.clear();
00427 Company.clear();
00428 DefaultCommunicationsMethod.clear();
00429 JobTitle.clear();
00430 PublicKey.clear();
00431 URL.clear();
00432 Prefix.clear();
00433 Notes.clear();
00434 UserDefined1.clear();
00435 UserDefined2.clear();
00436 UserDefined3.clear();
00437 UserDefined4.clear();
00438 Image.clear();
00439 Nickname.clear();
00440
00441 Birthday.Clear();
00442 Anniversary.Clear();
00443
00444 WorkAddress.Clear();
00445 HomeAddress.Clear();
00446
00447 Categories.clear();
00448
00449 GroupLinks.clear();
00450 Unknowns.clear();
00451
00452 m_FirstNameSeen = false;
00453 }
00454
00455 std::string Contact::GetDescription() const
00456 {
00457 return GetFullName();
00458 }
00459
00460
00461
00462
00463
00464
00465 std::string Contact::GetFullName() const
00466 {
00467 std::string Full = FirstName;
00468 if( Full.size() && LastName.size() )
00469 Full += " ";
00470 Full += LastName;
00471 return Full;
00472 }
00473
00474
00475
00476
00477
00478
00479
00480 const std::string& Contact::GetEmail(unsigned int index) const
00481 {
00482 static const std::string blank;
00483 if( index < EmailAddresses.size() )
00484 return EmailAddresses[index];
00485 return blank;
00486 }
00487
00488 void Contact::Dump(std::ostream &os) const
00489 {
00490 ios::fmtflags oldflags = os.setf(ios::left);
00491 char fill = os.fill(' ');
00492
00493 os << "Contact: 0x" << setbase(16) << GetID()
00494 << " (" << (unsigned int)RecType << ")\n";
00495
00496
00497 os << " " << setw(20) << "FirstName";
00498 os << ": " << FirstName << "\n";
00499 os << " " << setw(20) << "LastName";
00500 os << ": " << LastName << "\n";
00501
00502
00503 EmailList::const_iterator eai = EmailAddresses.begin();
00504 for( ; eai != EmailAddresses.end(); ++eai ) {
00505 if( eai->size() ) {
00506 os << " Email : " << *eai << "\n";
00507 }
00508 }
00509
00510
00511 for( FieldLink<Contact> *b = ContactFieldLinks;
00512 b->type != CFC_INVALID_FIELD;
00513 b++ )
00514 {
00515
00516
00517 if( b->type == CFC_IMAGE )
00518 continue;
00519
00520 const std::string *pField = 0;
00521 if( b->strMember ) {
00522 pField = &(this->*(b->strMember));
00523 }
00524 else if( b->postMember && b->postField ) {
00525 pField = &((this->*(b->postMember)).*(b->postField));
00526 }
00527
00528
00529 if( pField && pField->size() ) {
00530 os << " " << setw(20) << b->name;
00531 os << ": " << Cr2LfWrapper(*pField) << "\n";
00532 }
00533 }
00534
00535 if( Categories.size() ) {
00536 string display;
00537 Categories.CategoryList2Str(display);
00538 os << " Categories : " << display << "\n";
00539 }
00540
00541
00542 if( Birthday.HasData() ) {
00543 os << " Birthday : " << Birthday << "\n";
00544 }
00545 if( Anniversary.HasData() ) {
00546 os << " Anniversary : " << Anniversary << "\n";
00547 }
00548
00549
00550 GroupLinksType::const_iterator
00551 gb = GroupLinks.begin(), ge = GroupLinks.end();
00552 if( gb != ge )
00553 os << " GroupLinks:\n";
00554 for( ; gb != ge; gb++ ) {
00555 os << " ID: 0x" << setbase(16) << gb->Link << "\n";
00556 }
00557
00558
00559 if( Image.size() ) {
00560 Data image(Image.data(), Image.size());
00561 os << " Photo image:\n";
00562 os << image << "\n";
00563 }
00564
00565
00566 os << Unknowns;
00567
00568
00569 os.flags(oldflags);
00570 os.fill(fill);
00571 }
00572
00573 bool Contact::operator<(const Contact &other) const
00574 {
00575
00576
00577
00578
00579
00580
00581
00582 int cmp = LastName.compare(other.LastName);
00583 if( cmp == 0 )
00584 cmp = FirstName.compare(other.FirstName);
00585 if( cmp == 0 )
00586 cmp = Company.compare(other.Company);
00587 return cmp < 0;
00588 }
00589
00590 void Contact::SplitName(const std::string &full, std::string &first, std::string &last)
00591 {
00592 first.clear();
00593 last.clear();
00594
00595 string::size_type pos = full.find_last_of(' ');
00596 if( pos != string::npos ) {
00597
00598 last = full.c_str() + pos + 1;
00599 first = full.substr(0, pos);
00600 }
00601 else {
00602
00603 first = full.substr(0);
00604 }
00605 }
00606
00607 }
00608