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