- refactored record parser classes into separate files for each
[barry.git] / src / r_contact.cc
blob6bcfa22f6e8c68f2e4aea58587a3389fdec6898f
1 ///
2 /// \file r_contact.cc
3 /// Blackberry database record parser class for contact records.
4 ///
6 /*
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"
24 #include "protocol.h"
25 #include "protostructs.h"
26 #include "data.h"
27 #include "time.h"
28 #include "error.h"
29 #include "endian.h"
30 #include <ostream>
31 #include <iomanip>
32 #include <time.h>
33 #include <stdexcept>
35 #define __DEBUG_MODE__
36 #include "debug.h"
38 using namespace std;
39 using namespace Barry::Protocol;
41 namespace Barry {
44 ///////////////////////////////////////////////////////////////////////////////
45 // Contact class
47 // Contact field codes
48 #define CFC_EMAIL 1
49 #define CFC_PHONE 2
50 #define CFC_FAX 3
51 #define CFC_WORK_PHONE 6
52 #define CFC_HOME_PHONE 7
53 #define CFC_MOBILE_PHONE 8
54 #define CFC_PAGER 9
55 #define CFC_PIN 10
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
66 #define CFC_CITY 38
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 }
138 Contact::Contact()
139 : RecType(Contact::GetDefaultRecType()),
140 RecordId(0),
141 m_FirstNameSeen(false)
145 Contact::~Contact()
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
157 return begin;
159 if( !btohs(field->size) ) // if field has no size, something's up
160 return begin;
162 // cycle through the type table
163 for( FieldLink<Contact> *b = ContactFieldLinks;
164 b->type != CFC_INVALID_FIELD;
165 b++ )
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 )
177 case CFC_NAME: {
178 // can be used multiple times, for first/last names
179 std::string *name;
180 if( FirstName.size() || m_FirstNameSeen ) {
181 // first name already filled, use last name
182 name = &LastName;
183 m_FirstNameSeen = false;
185 else {
186 name = &FirstName;
187 m_FirstNameSeen = true;
190 name->assign((const char*)field->u.raw, btohs(field->size)-1);
192 return begin;
194 case CFC_GROUP_LINK:
195 // just add the unique ID to the list
196 GroupLinks.push_back(
197 GroupLink(field->u.link.uniqueId,
198 field->u.link.unknown));
199 return begin;
201 case CFC_GROUP_FLAG:
202 // ignore the group flag... the presense of group link items
203 // behaves as the flag in this class
204 return begin;
207 // if still not handled, add to the Unknowns list
208 UnknownField uf;
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
214 return begin;
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
236 // BuildFields
238 /// Build fields part of record
240 void Contact::BuildFields(Data &data, size_t &offset) const
242 data.Zap();
244 // check if this is a group link record, and if so, output
245 // the group flag
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;
258 b++ )
260 // print only fields with data
261 const std::string &field = this->*(b->strMember);
262 if( field.size() ) {
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();
291 Email.clear();
292 Phone.clear();
293 Fax.clear();
294 WorkPhone.clear();
295 HomePhone.clear();
296 MobilePhone.clear();
297 Pager.clear();
298 PIN.clear();
299 Radio.clear();
300 WorkPhone2.clear();
301 HomePhone2.clear();
302 OtherPhone.clear();
303 FirstName.clear();
304 LastName.clear();
305 Company.clear();
306 DefaultCommunicationsMethod.clear();
307 Address1.clear();
308 Address2.clear();
309 Address3.clear();
310 City.clear();
311 Province.clear();
312 PostalCode.clear();
313 Country.clear();
314 Title.clear();
315 PublicKey.clear();
316 URL.clear();
317 Prefix.clear();
318 HomeAddress1.clear();
319 HomeAddress2.clear();
320 HomeAddress3.clear();
321 Notes.clear();
322 UserDefined1.clear();
323 UserDefined2.clear();
324 UserDefined3.clear();
325 UserDefined4.clear();
326 HomeCity.clear();
327 HomeProvince.clear();
328 HomePostalCode.clear();
329 HomeCountry.clear();
330 Image.clear();
332 GroupLinks.clear();
333 Unknowns.clear();
335 m_FirstNameSeen = false;
339 // GetPostalAddress
341 /// Format a mailing address, handling missing fields.
343 std::string Contact::GetPostalAddress() const
345 std::string address = Address1;
346 if( Address2.size() ) {
347 if( address.size() )
348 address += "\n";
349 address += Address2;
351 if( Address3.size() ) {
352 if( address.size() )
353 address += "\n";
354 address += Address3;
356 if( address.size() )
357 address += "\n";
358 if( City.size() )
359 address += City + " ";
360 if( Province.size() )
361 address += Province + " ";
362 if( Country.size() )
363 address += Country;
364 if( address.size() )
365 address += "\n";
366 if( PostalCode.size() )
367 address += PostalCode;
369 return address;
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;
389 b++ )
391 // print only fields with data
392 const std::string &field = this->*(b->strMember);
393 if( field.size() ) {
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();
402 if( gb != ge )
403 os << " GroupLinks:\n";
404 for( ; gb != ge; gb++ ) {
405 os << " ID: 0x" << setbase(16) << gb->Link << "\n";
408 // and finally print unknowns
409 os << Unknowns;
411 // cleanup the stream
412 os.flags(oldflags);
413 os.fill(fill);
416 void Contact::SplitName(const std::string &full, std::string &first, std::string &last)
418 first.clear();
419 last.clear();
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);
427 else {
428 // no space, assume only first name
429 first = full.substr(0);
434 } // namespace Barry