3 /// Blackberry database record parser class for contact records.
7 Copyright (C) 2005-2007, Net Direct Inc. (http://www.netdirect.ca/)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
22 #include "r_contact.h"
23 #include "record-internal.h"
25 #include "protostructs.h"
35 #define __DEBUG_MODE__
39 using namespace Barry::Protocol
;
44 ///////////////////////////////////////////////////////////////////////////////
47 // Contact field codes
51 #define CFC_WORK_PHONE 6
52 #define CFC_HOME_PHONE 7
53 #define CFC_MOBILE_PHONE 8
56 #define CFC_RADIO 14 // 0x0e
57 #define CFC_WORK_PHONE_2 16 // 0x10
58 #define CFC_HOME_PHONE_2 17 // 0x11
59 #define CFC_OTHER_PHONE 18 // 0x12
60 #define CFC_NAME 32 // 0x20 used twice, in first/last name order
61 #define CFC_COMPANY 33
62 #define CFC_DEFAULT_COMM_METHOD 34
63 #define CFC_ADDRESS1 35
64 #define CFC_ADDRESS2 36
65 #define CFC_ADDRESS3 37
67 #define CFC_PROVINCE 39
68 #define CFC_POSTAL_CODE 40
69 #define CFC_COUNTRY 41
70 #define CFC_TITLE 42 // 0x2a
71 #define CFC_PUBLIC_KEY 43
72 #define CFC_GROUP_FLAG 44
73 #define CFC_GROUP_LINK 52
74 #define CFC_URL 54 // 0x36
75 #define CFC_PREFIX 55 // 0x37
76 #define CFC_CATEGORY 59 // 0x3B
77 #define CFC_HOME_ADDRESS1 61 // 0x3D
78 #define CFC_HOME_ADDRESS2 62 // 0x3E
79 // If the address 3 isn't mapped then it appears
80 // in the same field as address2 with a space
81 #define CFC_HOME_ADDRESS3 63 // 0x3F
82 #define CFC_NOTES 64 // 0x40
83 #define CFC_USER_DEFINED_1 65 // 0x41
84 #define CFC_USER_DEFINED_2 66 // 0x42
85 #define CFC_USER_DEFINED_3 67 // 0x43
86 #define CFC_USER_DEFINED_4 68 // 0x44
87 #define CFC_HOME_CITY 69 // 0x45
88 #define CFC_HOME_PROVINCE 70 // 0x46
89 #define CFC_HOME_POSTAL_CODE 71 // 0x47
90 #define CFC_HOME_COUNTRY 72 // 0x48
91 #define CFC_IMAGE 77 // 0x4D
92 #define CFC_INVALID_FIELD 255
94 // Contact code to field table
95 FieldLink
<Contact
> ContactFieldLinks
[] = {
96 { CFC_EMAIL
, "Email", "mail",0, &Contact::Email
, 0, 0 },
97 { CFC_PHONE
, "Phone", 0,0, &Contact::Phone
, 0, 0 },
98 { CFC_FAX
, "Fax", "facsimileTelephoneNumber",0, &Contact::Fax
, 0, 0 },
99 { CFC_WORK_PHONE
, "WorkPhone", "telephoneNumber",0, &Contact::WorkPhone
, 0, 0 },
100 { CFC_HOME_PHONE
, "HomePhone", "homePhone",0, &Contact::HomePhone
, 0, 0 },
101 { CFC_MOBILE_PHONE
, "MobilePhone","mobile",0, &Contact::MobilePhone
, 0, 0 },
102 { CFC_PAGER
, "Pager", "pager",0, &Contact::Pager
, 0, 0 },
103 { CFC_PIN
, "PIN", 0,0, &Contact::PIN
, 0, 0 },
104 { CFC_RADIO
, "Radio", 0,0, &Contact::Radio
, 0, 0 },
105 { CFC_WORK_PHONE_2
, "WorkPhone2", 0,0, &Contact::WorkPhone2
, 0, 0 },
106 { CFC_HOME_PHONE_2
, "HomePhone2", 0,0, &Contact::HomePhone2
, 0, 0 },
107 { CFC_OTHER_PHONE
, "OtherPhone", 0,0, &Contact::OtherPhone
, 0, 0 },
108 { CFC_COMPANY
, "Company", "o",0, &Contact::Company
, 0, 0 },
109 { CFC_DEFAULT_COMM_METHOD
,"DefaultCommMethod",0,0, &Contact::DefaultCommunicationsMethod
, 0, 0 },
110 { CFC_ADDRESS1
, "Address1", 0,0, &Contact::Address1
, 0, 0 },
111 { CFC_ADDRESS2
, "Address2", 0,0, &Contact::Address2
, 0, 0 },
112 { CFC_ADDRESS3
, "Address3", 0,0, &Contact::Address3
, 0, 0 },
113 { CFC_CITY
, "City", "l",0, &Contact::City
, 0, 0 },
114 { CFC_PROVINCE
, "Province", "st",0, &Contact::Province
, 0, 0 },
115 { CFC_POSTAL_CODE
, "PostalCode", "postalCode",0, &Contact::PostalCode
, 0, 0 },
116 { CFC_COUNTRY
, "Country", "c", "country", &Contact::Country
, 0, 0 },
117 { CFC_TITLE
, "Title", "title",0, &Contact::Title
, 0, 0 },
118 { CFC_PUBLIC_KEY
, "PublicKey", 0,0, &Contact::PublicKey
, 0, 0 },
119 { CFC_URL
, "URL", 0,0, &Contact::URL
, 0, 0 },
120 { CFC_PREFIX
, "Prefix", 0,0, &Contact::Prefix
, 0, 0 },
121 { CFC_CATEGORY
, "Category", 0,0, &Contact::Category
, 0, 0 },
122 { CFC_HOME_ADDRESS1
,"HomeAddress1", 0,0, &Contact::HomeAddress1
, 0, 0 },
123 { CFC_HOME_ADDRESS2
,"HomeAddress2", 0,0, &Contact::HomeAddress2
, 0, 0 },
124 { CFC_HOME_ADDRESS3
,"HomeAddress3", 0,0, &Contact::HomeAddress3
, 0, 0 },
125 { CFC_NOTES
, "Notes", 0,0, &Contact::Notes
, 0, 0 },
126 { CFC_USER_DEFINED_1
, "UserDefined1", 0,0, &Contact::UserDefined1
, 0, 0 },
127 { CFC_USER_DEFINED_2
, "UserDefined2", 0,0, &Contact::UserDefined2
, 0, 0 },
128 { CFC_USER_DEFINED_3
, "UserDefined3", 0,0, &Contact::UserDefined3
, 0, 0 },
129 { CFC_USER_DEFINED_4
, "UserDefined4", 0,0, &Contact::UserDefined4
, 0, 0 },
130 { CFC_HOME_CITY
, "HomeCity", 0,0, &Contact::HomeCity
, 0, 0 },
131 { CFC_HOME_PROVINCE
,"HomeProvince", 0,0, &Contact::HomeProvince
, 0, 0 },
132 { CFC_HOME_POSTAL_CODE
, "HomePostalCode", 0,0, &Contact::HomePostalCode
, 0, 0 },
133 { CFC_HOME_COUNTRY
, "HomeCountry",0,0, &Contact::HomeCountry
, 0, 0 },
134 { CFC_IMAGE
, "Image", 0,0, &Contact::Image
, 0, 0 },
135 { CFC_INVALID_FIELD
,"EndOfList", 0, 0, 0 }
139 : RecType(Contact::GetDefaultRecType()),
141 m_FirstNameSeen(false)
149 const unsigned char* Contact::ParseField(const unsigned char *begin
,
150 const unsigned char *end
)
152 const CommonField
*field
= (const CommonField
*) begin
;
154 // advance and check size
155 begin
+= COMMON_FIELD_HEADER_SIZE
+ btohs(field
->size
);
156 if( begin
> end
) // if begin==end, we are ok
159 if( !btohs(field
->size
) ) // if field has no size, something's up
162 // cycle through the type table
163 for( FieldLink
<Contact
> *b
= ContactFieldLinks
;
164 b
->type
!= CFC_INVALID_FIELD
;
167 if( b
->type
== field
->type
) {
168 std::string
&s
= this->*(b
->strMember
);
169 s
.assign((const char *)field
->u
.raw
, btohs(field
->size
)-1);
170 return begin
; // done!
174 // if not found in the type table, check for special handling
175 switch( field
->type
)
178 // can be used multiple times, for first/last names
180 if( FirstName
.size() || m_FirstNameSeen
) {
181 // first name already filled, use last name
183 m_FirstNameSeen
= false;
187 m_FirstNameSeen
= true;
190 name
->assign((const char*)field
->u
.raw
, btohs(field
->size
)-1);
195 // just add the unique ID to the list
196 GroupLinks
.push_back(
197 GroupLink(field
->u
.link
.uniqueId
,
198 field
->u
.link
.unknown
));
202 // ignore the group flag... the presense of group link items
203 // behaves as the flag in this class
207 // if still not handled, add to the Unknowns list
209 uf
.type
= field
->type
;
210 uf
.data
.assign((const char*)field
->u
.raw
, btohs(field
->size
));
211 Unknowns
.push_back(uf
);
213 // return new pointer for next field
217 void Contact::ParseHeader(const Data
&data
, size_t &offset
)
219 // no header to parse in Contact records
222 // this is called by the RecordParser<> class, which checks size for us
223 void Contact::ParseFields(const Data
&data
, size_t &offset
)
225 const unsigned char *finish
= ParseCommonFields(*this,
226 data
.GetData() + offset
, data
.GetData() + data
.GetSize());
227 offset
+= finish
- (data
.GetData() + offset
);
230 void Contact::BuildHeader(Data
&data
, size_t &offset
) const
232 // no header in Contact records
238 /// Build fields part of record
240 void Contact::BuildFields(Data
&data
, size_t &offset
) const
244 // check if this is a group link record, and if so, output
246 if( GroupLinks
.size() )
247 BuildField(data
, offset
, CFC_GROUP_FLAG
, 'G');
249 // special fields not in type table
250 if( FirstName
.size() )
251 BuildField(data
, offset
, CFC_NAME
, FirstName
);
252 if( LastName
.size() )
253 BuildField(data
, offset
, CFC_NAME
, LastName
);
255 // cycle through the type table
256 for( FieldLink
<Contact
> *b
= ContactFieldLinks
;
257 b
->type
!= CFC_INVALID_FIELD
;
260 // print only fields with data
261 const std::string
&field
= this->*(b
->strMember
);
263 BuildField(data
, offset
, b
->type
, field
);
267 // save any group links
268 GroupLinksType::const_iterator
269 gb
= GroupLinks
.begin(), ge
= GroupLinks
.end();
270 for( ; gb
!= ge
; gb
++ ) {
271 Barry::Protocol::GroupLink link
;
272 link
.uniqueId
= htobl(gb
->Link
);
273 link
.unknown
= htobs(gb
->Unknown
);
274 BuildField(data
, offset
, CFC_GROUP_LINK
, link
);
277 // and finally save unknowns
278 UnknownsType::const_iterator
279 ub
= Unknowns
.begin(), ue
= Unknowns
.end();
280 for( ; ub
!= ue
; ub
++ ) {
281 BuildField(data
, offset
, ub
->type
, ub
->data
);
284 data
.ReleaseBuffer(offset
);
287 void Contact::Clear()
289 RecType
= Contact::GetDefaultRecType();
306 DefaultCommunicationsMethod
.clear();
318 HomeAddress1
.clear();
319 HomeAddress2
.clear();
320 HomeAddress3
.clear();
322 UserDefined1
.clear();
323 UserDefined2
.clear();
324 UserDefined3
.clear();
325 UserDefined4
.clear();
327 HomeProvince
.clear();
328 HomePostalCode
.clear();
335 m_FirstNameSeen
= false;
341 /// Format a mailing address, handling missing fields.
343 std::string
Contact::GetPostalAddress() const
345 std::string address
= Address1
;
346 if( Address2
.size() ) {
351 if( Address3
.size() ) {
359 address
+= City
+ " ";
360 if( Province
.size() )
361 address
+= Province
+ " ";
366 if( PostalCode
.size() )
367 address
+= PostalCode
;
372 void Contact::Dump(std::ostream
&os
) const
374 ios::fmtflags oldflags
= os
.setf(ios::left
);
375 char fill
= os
.fill(' ');
377 os
<< "Contact: 0x" << setbase(16) << GetID()
378 << " (" << (unsigned int)RecType
<< ")\n";
380 // special fields not in type table
381 os
<< " " << setw(20) << "FirstName";
382 os
<< ": " << FirstName
<< "\n";
383 os
<< " " << setw(20) << "LastName";
384 os
<< ": " << LastName
<< "\n";
386 // cycle through the type table
387 for( FieldLink
<Contact
> *b
= ContactFieldLinks
;
388 b
->type
!= CFC_INVALID_FIELD
;
391 // print only fields with data
392 const std::string
&field
= this->*(b
->strMember
);
394 os
<< " " << setw(20) << b
->name
;
395 os
<< ": " << field
<< "\n";
399 // print any group links
400 GroupLinksType::const_iterator
401 gb
= GroupLinks
.begin(), ge
= GroupLinks
.end();
403 os
<< " GroupLinks:\n";
404 for( ; gb
!= ge
; gb
++ ) {
405 os
<< " ID: 0x" << setbase(16) << gb
->Link
<< "\n";
408 // and finally print unknowns
411 // cleanup the stream
416 void Contact::SplitName(const std::string
&full
, std::string
&first
, std::string
&last
)
421 string::size_type pos
= full
.find_last_of(' ');
422 if( pos
!= string::npos
) {
423 // has space, assume last word is last name
424 last
= full
.c_str() + pos
+ 1;
425 first
= full
.substr(0, pos
);
428 // no space, assume only first name
429 first
= full
.substr(0);