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