r_contact.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       r_contact.cc
00003 ///             Blackberry database record parser class for contact records.
00004 ///
00005 
00006 /*
00007     Copyright (C) 2005-2010, Net Direct Inc. (http://www.netdirect.ca/)
00008 
00009     This program is free software; you can redistribute it and/or modify
00010     it under the terms of the GNU General Public License as published by
00011     the Free Software Foundation; either version 2 of the License, or
00012     (at your option) any later version.
00013 
00014     This program is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00017 
00018     See the GNU General Public License in the COPYING file at the
00019     root directory of this project for more details.
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 // Contact class
00048 
00049 // Contact field codes
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   // If the address 3 isn't mapped then it appears
00082   // in the same field as address2 with a space
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_UNIQUEID            85      // 0x55
00097 #define CFC_INVALID_FIELD       255
00098 
00099 // Contact code to field table
00100 static FieldLink<Contact> ContactFieldLinks[] = {
00101    { CFC_PHONE,        "Phone",      0,0,                 &Contact::Phone, 0, 0, 0, 0, true },
00102    { CFC_FAX,          "Fax",        "facsimileTelephoneNumber",0, &Contact::Fax, 0, 0, 0, 0, true },
00103    { CFC_WORK_PHONE,   "WorkPhone",  "telephoneNumber",0, &Contact::WorkPhone, 0, 0, 0, 0, true },
00104    { CFC_HOME_PHONE,   "HomePhone",  "homePhone",0,       &Contact::HomePhone, 0, 0, 0, 0, true },
00105    { CFC_MOBILE_PHONE, "MobilePhone","mobile",0,          &Contact::MobilePhone, 0, 0, 0, 0, true },
00106    { CFC_PAGER,        "Pager",      "pager",0,           &Contact::Pager, 0, 0, 0, 0, true },
00107    { CFC_PIN,          "PIN",        0,0,                 &Contact::PIN, 0, 0, 0, 0, true },
00108    { CFC_RADIO,        "Radio",      0,0,                 &Contact::Radio, 0, 0, 0, 0, true },
00109    { CFC_WORK_PHONE_2, "WorkPhone2", 0,0,                 &Contact::WorkPhone2, 0, 0, 0, 0, true },
00110    { CFC_HOME_PHONE_2, "HomePhone2", 0,0,                 &Contact::HomePhone2, 0, 0, 0, 0, true },
00111    { CFC_OTHER_PHONE,  "OtherPhone", 0,0,                 &Contact::OtherPhone, 0, 0, 0, 0, true },
00112    { CFC_COMPANY,      "Company",    "o",0,               &Contact::Company, 0, 0, 0, 0, true },
00113    { CFC_DEFAULT_COMM_METHOD,"DefaultCommMethod",0,0,     &Contact::DefaultCommunicationsMethod, 0, 0, 0, 0, true },
00114    { CFC_ADDRESS1,     "WorkAddress1",   0,0,             0, 0, 0, &Contact::WorkAddress, &PostalAddress::Address1, true },
00115    { CFC_ADDRESS2,     "WorkAddress2",   0,0,             0, 0, 0, &Contact::WorkAddress, &PostalAddress::Address2, true },
00116    { CFC_ADDRESS3,     "WorkAddress3",   0,0,             0, 0, 0, &Contact::WorkAddress, &PostalAddress::Address3, true },
00117    { CFC_CITY,         "WorkCity",       "l",0,           0, 0, 0, &Contact::WorkAddress, &PostalAddress::City, true },
00118    { CFC_PROVINCE,     "WorkProvince",   "st",0,          0, 0, 0, &Contact::WorkAddress, &PostalAddress::Province, true },
00119    { CFC_POSTAL_CODE,  "WorkPostalCode", "postalCode",0,  0, 0, 0, &Contact::WorkAddress, &PostalAddress::PostalCode, true },
00120    { CFC_COUNTRY,      "WorkCountry",    "c", "country",  0, 0, 0, &Contact::WorkAddress, &PostalAddress::Country, true },
00121    { CFC_TITLE,        "JobTitle",   "title",0,           &Contact::JobTitle, 0, 0, 0, 0, true },
00122    { CFC_PUBLIC_KEY,   "PublicKey",  0,0,                 &Contact::PublicKey, 0, 0, 0, 0, false },
00123    { CFC_URL,          "URL",        0,0,                 &Contact::URL, 0, 0, 0, 0, true },
00124    { CFC_PREFIX,       "Prefix",     0,0,                 &Contact::Prefix, 0, 0, 0, 0, true },
00125    { CFC_HOME_ADDRESS1,"HomeAddress1", 0,0,               0, 0, 0, &Contact::HomeAddress, &PostalAddress::Address1, true },
00126    { CFC_HOME_ADDRESS2,"HomeAddress2", 0,0,               0, 0, 0, &Contact::HomeAddress, &PostalAddress::Address2, true },
00127    { CFC_HOME_ADDRESS3,"HomeAddress3", 0,0,               0, 0, 0, &Contact::HomeAddress, &PostalAddress::Address3, true },
00128    { CFC_NOTES,        "Notes",      0,0,                 &Contact::Notes, 0, 0, 0, 0, true },
00129    { CFC_USER_DEFINED_1, "UserDefined1", 0,0,             &Contact::UserDefined1, 0, 0, 0, 0, true },
00130    { CFC_USER_DEFINED_2, "UserDefined2", 0,0,             &Contact::UserDefined2, 0, 0, 0, 0, true },
00131    { CFC_USER_DEFINED_3, "UserDefined3", 0,0,             &Contact::UserDefined3, 0, 0, 0, 0, true },
00132    { CFC_USER_DEFINED_4, "UserDefined4", 0,0,             &Contact::UserDefined4, 0, 0, 0, 0, true },
00133    { CFC_HOME_CITY,    "HomeCity",   0,0,                 0, 0, 0, &Contact::HomeAddress, &PostalAddress::City, true },
00134    { CFC_HOME_PROVINCE,"HomeProvince", 0,0,               0, 0, 0, &Contact::HomeAddress, &PostalAddress::Province, true },
00135    { CFC_HOME_POSTAL_CODE, "HomePostalCode", 0,0,         0, 0, 0, &Contact::HomeAddress, &PostalAddress::PostalCode, true },
00136    { CFC_HOME_COUNTRY, "HomeCountry",0,0,                 0, 0, 0, &Contact::HomeAddress, &PostalAddress::Country, true },
00137    { CFC_IMAGE,        "Image",      0,0,                 &Contact::Image, 0, 0, 0, 0, false },
00138    { CFC_INVALID_FIELD,"EndOfList",  0, 0, 0, 0, 0, 0, 0, false }
00139 };
00140 
00141 Contact::Contact()
00142         : RecType(Contact::GetDefaultRecType()),
00143         RecordId(0),
00144         m_FirstNameSeen(false)
00145 {
00146 }
00147 
00148 Contact::~Contact()
00149 {
00150 }
00151 
00152 const unsigned char* Contact::ParseField(const unsigned char *begin,
00153                                          const unsigned char *end,
00154                                          const IConverter *ic)
00155 {
00156         const CommonField *field = (const CommonField *) begin;
00157 
00158         // advance and check size
00159         begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
00160         if( begin > end )               // if begin==end, we are ok
00161                 return begin;
00162 
00163         if( !btohs(field->size) )       // if field has no size, something's up
00164                 return begin;
00165 
00166         // cycle through the type table
00167         for(    FieldLink<Contact> *b = ContactFieldLinks;
00168                 b->type != CFC_INVALID_FIELD;
00169                 b++ )
00170         {
00171                 if( b->type == field->type ) {
00172                         if( b->strMember ) {
00173                                 std::string &s = this->*(b->strMember);
00174                                 s = ParseFieldString(field);
00175                                 if( b->iconvNeeded && ic )
00176                                         s = ic->FromBB(s);
00177                                 return begin;   // done!
00178                         }
00179                         else if( b->postMember && b->postField ) {
00180                                 std::string &s = (this->*(b->postMember)).*(b->postField);
00181                                 s = ParseFieldString(field);
00182                                 if( b->iconvNeeded && ic )
00183                                         s = ic->FromBB(s);
00184                                 return begin;
00185                         }
00186                         else {
00187                                 break;  // fall through to special handling
00188                         }
00189                 }
00190         }
00191 
00192         // if not found in the type table, check for special handling
00193         switch( field->type )
00194         {
00195         case CFC_EMAIL: {
00196                 std::string s = ParseFieldString(field);
00197                 if( ic )
00198                         s = ic->FromBB(s);
00199                 EmailAddresses.push_back( s );
00200                 }
00201                 return begin;
00202 
00203         case CFC_NAME: {
00204                 // can be used multiple times, for first/last names
00205                 std::string *name;
00206                 if( FirstName.size() || m_FirstNameSeen ) {
00207                         // first name already filled, use last name
00208                         name = &LastName;
00209                         m_FirstNameSeen = false;
00210                 }
00211                 else {
00212                         name = &FirstName;
00213                         m_FirstNameSeen = true;
00214                 }
00215 
00216                 *name = ParseFieldString(field);
00217                 if( ic )
00218                         *name = ic->FromBB(*name);
00219                 }
00220                 return begin;
00221 
00222         case CFC_GROUP_LINK:
00223                 // just add the unique ID to the list
00224                 GroupLinks.push_back(
00225                         GroupLink(field->u.link.uniqueId,
00226                                 field->u.link.unknown));
00227                 return begin;
00228 
00229         case CFC_GROUP_FLAG:
00230                 // ignore the group flag... the presense of group link items
00231                 // behaves as the flag in this class
00232                 return begin;
00233 
00234         case CFC_CATEGORY: {
00235                 std::string catstring = ParseFieldString(field);
00236                 if( ic )
00237                         catstring = ic->FromBB(catstring);
00238                 Categories.CategoryStr2List(catstring);
00239                 }
00240                 return begin;
00241 
00242         case CFC_BIRTHDAY: {
00243                 std::string bstring = ParseFieldString(field);
00244                 Birthday.FromBBString(bstring);
00245                 }
00246                 return begin;
00247 
00248         case CFC_ANNIVERSARY: {
00249                 std::string astring = ParseFieldString(field);
00250                 Anniversary.FromBBString(astring);
00251                 }
00252                 return begin;
00253         }
00254 
00255         // if still not handled, add to the Unknowns list
00256         UnknownField uf;
00257         uf.type = field->type;
00258         uf.data.assign((const char*)field->u.raw, btohs(field->size));
00259         Unknowns.push_back(uf);
00260 
00261         // return new pointer for next field
00262         return begin;
00263 }
00264 
00265 void Contact::ParseHeader(const Data &data, size_t &offset)
00266 {
00267         // no header to parse in Contact records
00268 }
00269 
00270 // this is called by the RecordParser<> class, which checks size for us
00271 void Contact::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
00272 {
00273         const unsigned char *finish = ParseCommonFields(*this,
00274                 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
00275         offset += finish - (data.GetData() + offset);
00276 }
00277 
00278 void Contact::BuildHeader(Data &data, size_t &offset) const
00279 {
00280         // no header in Contact records
00281 }
00282 
00283 //
00284 // BuildFields
00285 //
00286 /// Build fields part of record
00287 ///
00288 void Contact::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
00289 {
00290         data.Zap();
00291 
00292         // Sanity check: the Blackberry requires at least a name or
00293         // a company name for each address record.
00294         if( !GetFullName().size() && !Company.size() )
00295                 throw BadData("Contact must have name or company name.");
00296 
00297         // check if this is a group link record, and if so, output
00298         // the group flag
00299         if( GroupLinks.size() )
00300                 BuildField(data, offset, CFC_GROUP_FLAG, 'G');
00301 
00302         // special fields not in type table
00303         if( FirstName.size() ) {
00304                 std::string s = ic ? ic->ToBB(FirstName) : FirstName;
00305                 BuildField(data, offset, CFC_NAME, s);
00306         }
00307         if( LastName.size() ) {
00308                 if( !FirstName.size() ) {
00309                         // order matters with first/last name, and if
00310                         // last name exists, and first name doesn't,
00311                         // insert blank first name ahead of it
00312                         BuildField(data, offset, CFC_NAME, "");
00313                 }
00314                 BuildField(data, offset, CFC_NAME, ic ? ic->ToBB(LastName) : LastName);
00315         }
00316 
00317 //      FIXME
00318 //      // add unknown data
00319 //      char buffer[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
00320 //      BuildField(data, offset, 0x54, buffer, 8);
00321 
00322         // With the BlackBerry Storm, I have to add this entry.
00323         // Otherwise the uniqueId of this contact is reseted !
00324         // The device seems accept the multiple contact with the same uniqueId,
00325         // but the synchronization process uses this uniqueId to identify the contact.
00326         // add uniqueId
00327         BuildField(data, offset, CFC_UNIQUEID, RecordId);
00328 
00329         // add all email addresses
00330         EmailList::const_iterator eai = EmailAddresses.begin();
00331         for( ; eai != EmailAddresses.end(); ++eai ) {
00332                 if( eai->size() ) {
00333                         BuildField(data, offset, CFC_EMAIL, ic ? ic->ToBB(*eai) : *eai);
00334                 }
00335         }
00336 
00337         // cycle through the type table
00338         for(    FieldLink<Contact> *b = ContactFieldLinks;
00339                 b->type != CFC_INVALID_FIELD;
00340                 b++ )
00341         {
00342                 // print only fields with data
00343                 if( b->strMember ) {
00344                         const std::string &field = this->*(b->strMember);
00345                         if( field.size() ) {
00346                                 std::string s = (b->iconvNeeded && ic) ? ic->ToBB(field) : field;
00347                                 BuildField(data, offset, b->type, s);
00348                         }
00349                 }
00350                 else if( b->postMember && b->postField ) {
00351                         const std::string &field = (this->*(b->postMember)).*(b->postField);
00352                         if( field.size() ) {
00353                                 std::string s = (b->iconvNeeded && ic) ? ic->ToBB(field) : field;
00354                                 BuildField(data, offset, b->type, s);
00355                         }
00356                 }
00357         }
00358 
00359         // save any group links
00360         GroupLinksType::const_iterator
00361                 gb = GroupLinks.begin(), ge = GroupLinks.end();
00362         for( ; gb != ge; gb++ ) {
00363                 Barry::Protocol::GroupLink link;
00364                 link.uniqueId = htobl(gb->Link);
00365                 link.unknown = htobs(gb->Unknown);
00366                 BuildField(data, offset, CFC_GROUP_LINK, link);
00367         }
00368 
00369         // save categories
00370         if( Categories.size() ) {
00371                 string store;
00372                 Categories.CategoryList2Str(store);
00373                 BuildField(data, offset, CFC_CATEGORY, ic ? ic->ToBB(store) : store);
00374         }
00375 
00376         // save Birthday and Anniversary
00377         if( Birthday.HasData() )
00378                 BuildField(data, offset, CFC_BIRTHDAY, Birthday.ToBBString());
00379         if( Anniversary.HasData() )
00380                 BuildField(data, offset, CFC_ANNIVERSARY, Anniversary.ToBBString());
00381 
00382         // and finally save unknowns
00383         UnknownsType::const_iterator
00384                 ub = Unknowns.begin(), ue = Unknowns.end();
00385         for( ; ub != ue; ub++ ) {
00386                 BuildField(data, offset, *ub);
00387         }
00388 
00389         data.ReleaseBuffer(offset);
00390 }
00391 
00392 void Contact::Clear()
00393 {
00394         RecType = Contact::GetDefaultRecType();
00395 
00396         EmailAddresses.clear();
00397         Phone.clear();
00398         Fax.clear();
00399         WorkPhone.clear();
00400         HomePhone.clear();
00401         MobilePhone.clear();
00402         Pager.clear();
00403         PIN.clear();
00404         Radio.clear();
00405         WorkPhone2.clear();
00406         HomePhone2.clear();
00407         OtherPhone.clear();
00408         FirstName.clear();
00409         LastName.clear();
00410         Company.clear();
00411         DefaultCommunicationsMethod.clear();
00412         JobTitle.clear();
00413         PublicKey.clear();
00414         URL.clear();
00415         Prefix.clear();
00416         Notes.clear();
00417         UserDefined1.clear();
00418         UserDefined2.clear();
00419         UserDefined3.clear();
00420         UserDefined4.clear();
00421         Image.clear();
00422 
00423         Birthday.Clear();
00424         Anniversary.Clear();
00425 
00426         WorkAddress.Clear();
00427         HomeAddress.Clear();
00428 
00429         Categories.clear();
00430 
00431         GroupLinks.clear();
00432         Unknowns.clear();
00433 
00434         m_FirstNameSeen = false;
00435 }
00436 
00437 //
00438 // GetFullName
00439 //
00440 /// Helper function that returns a formatted full name
00441 ///
00442 std::string Contact::GetFullName() const
00443 {
00444         std::string Full = FirstName;
00445         if( Full.size() && LastName.size() )
00446                 Full += " ";
00447         Full += LastName;
00448         return Full;
00449 }
00450 
00451 //
00452 // GetEmail
00453 //
00454 /// Helper function that always returns a valid string.  The string
00455 /// may be empty if there is no address at the specified index.
00456 ///
00457 const std::string& Contact::GetEmail(unsigned int index) const
00458 {
00459         static const std::string blank;
00460         if( index < EmailAddresses.size() )
00461                 return EmailAddresses[index];
00462         return blank;
00463 }
00464 
00465 void Contact::Dump(std::ostream &os) const
00466 {
00467         ios::fmtflags oldflags = os.setf(ios::left);
00468         char fill = os.fill(' ');
00469 
00470         os << "Contact: 0x" << setbase(16) << GetID()
00471                 << " (" << (unsigned int)RecType << ")\n";
00472 
00473         // special fields not in type table
00474         os << "    " << setw(20) << "FirstName";
00475         os << ": " << FirstName << "\n";
00476         os << "    " << setw(20) << "LastName";
00477         os << ": " << LastName << "\n";
00478 
00479         // cycle through email addresses
00480         EmailList::const_iterator eai = EmailAddresses.begin();
00481         for( ; eai != EmailAddresses.end(); ++eai ) {
00482                 if( eai->size() ) {
00483                         os << "    Email               : " << *eai << "\n";
00484                 }
00485         }
00486 
00487         // cycle through the type table
00488         for(    FieldLink<Contact> *b = ContactFieldLinks;
00489                 b->type != CFC_INVALID_FIELD;
00490                 b++ )
00491         {
00492                 // special case: don't dump the raw image data, but
00493                 // leave that for a special hex dump
00494                 if( b->type == CFC_IMAGE )
00495                         continue;
00496 
00497                 const std::string *pField = 0;
00498                 if( b->strMember ) {
00499                         pField = &(this->*(b->strMember));
00500                 }
00501                 else if( b->postMember && b->postField ) {
00502                         pField = &((this->*(b->postMember)).*(b->postField));
00503                 }
00504 
00505                 // print only fields with data
00506                 if( pField && pField->size() ) {
00507                         os << "    " << setw(20) << b->name;
00508                         os << ": " << *pField << "\n";
00509                 }
00510         }
00511 
00512         if( Categories.size() ) {
00513                 string display;
00514                 Categories.CategoryList2Str(display);
00515                 os << "    Categories          : " << display << "\n";
00516         }
00517 
00518         // print Birthday and Anniversary
00519         if( Birthday.HasData() ) {
00520                 os << "    Birthday            : " << Birthday << "\n";
00521         }
00522         if( Anniversary.HasData() ) {
00523                 os << "    Anniversary         : " << Anniversary << "\n";
00524         }
00525 
00526         // print any group links
00527         GroupLinksType::const_iterator
00528                 gb = GroupLinks.begin(), ge = GroupLinks.end();
00529         if( gb != ge )
00530                 os << "    GroupLinks:\n";
00531         for( ; gb != ge; gb++ ) {
00532                 os << "        ID: 0x" << setbase(16) << gb->Link << "\n";
00533         }
00534 
00535         // print Image in hex dump format, if available
00536         if( Image.size() ) {
00537                 Data image(Image.data(), Image.size());
00538                 os << "    Photo image:\n";
00539                 os << image << "\n";
00540         }
00541 
00542         // and finally print unknowns
00543         os << Unknowns;
00544 
00545         // cleanup the stream
00546         os.flags(oldflags);
00547         os.fill(fill);
00548 }
00549 
00550 void Contact::SplitName(const std::string &full, std::string &first, std::string &last)
00551 {
00552         first.clear();
00553         last.clear();
00554 
00555         string::size_type pos = full.find_last_of(' ');
00556         if( pos != string::npos ) {
00557                 // has space, assume last word is last name
00558                 last = full.c_str() + pos + 1;
00559                 first = full.substr(0, pos);
00560         }
00561         else {
00562                 // no space, assume only first name
00563                 first = full.substr(0);
00564         }
00565 }
00566 
00567 } // namespace Barry
00568 

Generated on 29 Mar 2010 for Barry by  doxygen 1.6.1