debian: updated Build-Depends
[barry.git] / src / record.cc
blob2e5bc38c96a16ef18a6ca1b4e1611f011ae27857
1 ///
2 /// \file record.cc
3 /// Misc. Blackberry database record helper classes and functions.
4 /// Helps translate data from data packets to useful structures,
5 /// and back.
6 ///
8 /*
9 Copyright (C) 2005-2012, Net Direct Inc. (http://www.netdirect.ca/)
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 See the GNU General Public License in the COPYING file at the
21 root directory of this project for more details.
24 #include "record.h"
25 #include "record-internal.h"
26 #include "protostructs.h"
27 #include "data.h"
28 #include "time.h"
29 #include "error.h"
30 #include "endian.h"
31 #include "trim.h"
32 #include "ios_state.h"
33 #include "parser.h"
34 #include <sstream>
35 #include <iomanip>
36 #include <time.h>
37 #include <string.h>
38 #include <stdio.h> // for sscanf()
39 #include <stdexcept>
41 // for DBNamedFieldCmp below
42 #include "recordtmpl.h"
43 #include "r_calendar.h"
44 #include "r_calllog.h"
45 #include "r_bookmark.h"
46 #include "r_contact.h"
47 #include "r_cstore.h"
48 #include "r_memo.h"
49 #include "r_message.h"
50 #include "r_servicebook.h"
51 #include "r_task.h"
52 #include "r_pin_message.h"
53 #include "r_saved_message.h"
54 #include "r_sms.h"
55 #include "r_folder.h"
56 #include "r_timezone.h"
57 #include "r_hhagent.h"
59 #define __DEBUG_MODE__
60 #include "debug.h"
62 using namespace std;
63 using namespace Barry::Protocol;
65 namespace Barry {
67 std::ostream& operator<< (std::ostream &os, const Cr2LfWrapper &str)
69 for( std::string::const_iterator i = str.m_str.begin();
70 i != str.m_str.end() && *i;
71 i++)
73 if( *i == '\r' )
74 os << '\n';
75 else
76 os << *i;
78 return os;
81 std::ostream& operator<< (std::ostream &os, const TimeT &t)
83 // strip the trailing newline
84 string output = ctime(&t.Time);
85 while( output.size() &&
86 (output[output.size()-1] == '\n' ||
87 output[output.size()-1] == '\r') )
89 output.resize(output.size() - 1);
92 os << output;
93 return os;
96 //////////////////////////////////////////////////////////////////////////////
97 // Field builder helper functions
99 void BuildField1900(Data &data, size_t &size, uint8_t type, time_t t)
101 size_t timesize = COMMON_FIELD_MIN1900_SIZE;
102 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + timesize;
103 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
104 CommonField *field = (CommonField *) pd;
106 field->size = htobs(timesize);
107 field->type = type;
108 field->u.min1900 = time2min(t);
110 size += fieldsize;
113 void BuildField(Data &data, size_t &size, uint8_t type, char c)
115 BuildField(data, size, type, (uint8_t)c);
118 void BuildField(Data &data, size_t &size, uint8_t type, uint8_t c)
120 size_t strsize = 1;
121 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
122 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
123 CommonField *field = (CommonField *) pd;
125 field->size = htobs(strsize);
126 field->type = type;
127 memcpy(field->u.raw, &c, strsize);
129 size += fieldsize;
132 void BuildField(Data &data, size_t &size, uint8_t type, uint16_t value)
134 size_t strsize = 2;
135 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
136 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
137 CommonField *field = (CommonField *) pd;
139 field->size = htobs(strsize);
140 field->type = type;
142 uint16_t store = htobs(value);
143 memcpy(field->u.raw, &store, strsize);
145 size += fieldsize;
148 void BuildField(Data &data, size_t &size, uint8_t type, uint32_t value)
150 size_t strsize = 4;
151 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
152 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
153 CommonField *field = (CommonField *) pd;
155 field->size = htobl(strsize);
156 field->type = type;
158 uint32_t store = htobl(value);
159 memcpy(field->u.raw, &store, strsize);
161 size += fieldsize;
164 void BuildField(Data &data, size_t &size, uint8_t type, uint64_t value)
166 size_t strsize = 8;
167 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
168 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
169 CommonField *field = (CommonField *) pd;
171 field->size = htobl(strsize);
172 field->type = type;
174 uint64_t store = htobll(value);
175 memcpy(field->u.raw, &store, strsize);
177 size += fieldsize;
180 void BuildField(Data &data, size_t &size, uint8_t type, const std::string &str)
182 // include null terminator
183 BuildField(data, size, type, str.c_str(), str.size() + 1);
186 void BuildField(Data &data, size_t &size, uint8_t type,
187 const void *buf, size_t bufsize)
189 // include null terminator
190 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + bufsize;
191 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
192 CommonField *field = (CommonField *) pd;
194 field->size = htobs(bufsize);
195 field->type = type;
196 memcpy(field->u.raw, buf, bufsize);
198 size += fieldsize;
201 void BuildField(Data &data, size_t &size, const Barry::UnknownField &field)
203 BuildField(data, size, field.type,
204 field.data.raw_data.data(), field.data.raw_data.size());
207 void BuildField(Data &data, size_t &size, uint8_t type, const Barry::Protocol::GroupLink &link)
209 size_t linksize = sizeof(Barry::Protocol::GroupLink);
210 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + linksize;
211 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
212 CommonField *field = (CommonField *) pd;
214 field->size = htobs(linksize);
215 field->type = type;
216 field->u.link = link;
218 size += fieldsize;
221 std::string ParseFieldString(const Barry::Protocol::CommonField *field)
223 // make no assumptions here, and pass the full size in as
224 // the maxlen, even though 99% of the time, it will be a null...
225 // this function can be used by non-null terminated strings as well
226 return ParseFieldString(field->u.raw, btohs(field->size));
229 std::string ParseFieldString(const void *data, uint16_t maxlen)
231 const char *str = (const char *)data;
233 // find last non-null character, since some fields
234 // can have multiple null terminators
235 while( maxlen && str[maxlen-1] == 0 )
236 maxlen--;
238 return std::string(str, maxlen);
242 ///////////////////////////////////////////////////////////////////////////////
243 // UnknownField
245 std::ostream& operator<< (std::ostream &os, const std::vector<UnknownField> &unknowns)
247 ios_format_state state(os);
249 std::vector<UnknownField>::const_iterator
250 ub = unknowns.begin(), ue = unknowns.end();
251 if( ub != ue )
252 os << " Unknowns:\n";
253 for( ; ub != ue; ub++ ) {
254 os << " Type: 0x" << setbase(16)
255 << (unsigned int) ub->type
256 << " Data:\n" << Data(ub->data.data(), ub->data.size());
258 return os;
262 ///////////////////////////////////////////////////////////////////////////////
263 // EmailList typedef
265 std::ostream& operator<< (std::ostream &os, const EmailList &list)
267 for( EmailList::const_iterator b = list.begin(), e = list.end();
268 b != e;
269 ++b )
271 if( b != list.begin() )
272 os << ", ";
273 os << *b;
275 return os;
278 ///////////////////////////////////////////////////////////////////////////////
279 // EmailAddress class
281 EmailAddress::EmailAddress(const std::string &complex_address)
283 size_t end = complex_address.rfind('>');
284 size_t start = complex_address.rfind('<');
285 if( start == string::npos || end == string::npos || start > end ) {
286 // simple address, add it
287 Email = complex_address;
288 Inplace::trim(Email);
290 else {
291 Name = complex_address.substr(0, start);
292 Inplace::trim(Name);
294 Email = complex_address.substr(start+1, end - start - 1);
295 Inplace::trim(Email);
299 std::ostream& operator<<(std::ostream &os, const EmailAddress &msga)
301 if( msga.Name.size() )
302 os << msga.Name << " <";
303 os << msga.Email;
304 if( msga.Name.size() )
305 os << ">";
306 return os;
310 ///////////////////////////////////////////////////////////////////////////////
311 // EmailAddressList class
313 std::string EmailAddressList::ToCommaSeparated() const
315 std::ostringstream oss;
316 oss << *this;
317 return oss.str();
320 /// Adds every email address found in the comma separated list.
321 /// Does not clear() first.
322 void EmailAddressList::AddCommaSeparated(const std::string &list)
324 istringstream iss(list);
325 string address;
326 iss >> ws;
328 while( getline(iss, address, ',') ) {
329 // trim any trailing whitespace in the address
330 size_t len = address.size();
331 while( len && ::isspace(address[len-1]) )
332 address.resize(len-1);
334 // add to list if anything left
335 if( address.size() ) {
336 EmailAddress ea(address);
337 push_back(ea);
342 std::ostream& operator<<(std::ostream &os, const EmailAddressList &elist)
344 for( EmailAddressList::const_iterator i = elist.begin(); i != elist.end(); ++i ) {
345 if( i != elist.begin() )
346 os << ", ";
347 os << *i;
349 return os;
353 ///////////////////////////////////////////////////////////////////////////////
354 // PostalAddress class
357 // GetLabel
359 /// Format a mailing address into a single string, handling missing fields.
361 std::string PostalAddress::GetLabel() const
363 std::string address = Address1;
364 if( Address2.size() ) {
365 if( address.size() )
366 address += "\n";
367 address += Address2;
369 if( Address3.size() ) {
370 if( address.size() )
371 address += "\n";
372 address += Address3;
374 if( address.size() )
375 address += "\n";
376 if( City.size() )
377 address += City + " ";
378 if( Province.size() )
379 address += Province + " ";
380 if( Country.size() )
381 address += Country;
382 if( address.size() )
383 address += "\n";
384 if( PostalCode.size() )
385 address += PostalCode;
387 return address;
390 void PostalAddress::Clear()
392 Address1.clear();
393 Address2.clear();
394 Address3.clear();
395 City.clear();
396 Province.clear();
397 PostalCode.clear();
398 Country.clear();
401 std::ostream& operator<<(std::ostream &os, const PostalAddress &post) {
402 os << post.GetLabel();
403 return os;
408 ///////////////////////////////////////////////////////////////////////////////
409 // Date class
411 Date::Date(const struct tm *timep)
413 FromTm(timep);
416 void Date::Clear()
418 Month = Day = Year = 0;
421 void Date::ToTm(struct tm *timep) const
423 memset(timep, 0, sizeof(tm));
424 timep->tm_year = Year - 1900;
425 timep->tm_mon = Month;
426 timep->tm_mday = Day;
429 std::string Date::ToYYYYMMDD() const
431 std::ostringstream oss;
432 // setfill and setw not sticky.
433 oss << setw(4) << setfill('0') << dec << Year
434 << setw(2) << setfill('0') << dec << (Month + 1)
435 << setw(2) << setfill('0') << dec << Day;
436 return oss.str();
440 // ToBBString
442 /// The Blackberry stores Birthday and Anniversary date fields
443 /// with the format: DD/MM/YYYY
445 std::string Date::ToBBString() const
447 std::ostringstream oss;
448 // setw() ain't 'sticky'!
449 oss << setw(2) << setfill('0') << dec << Day << '/'
450 << setw(2) << setfill('0') << dec << (Month + 1) << '/'
451 << setw(2) << setfill('0') << dec << Year;
452 return oss.str();
455 bool Date::FromTm(const struct tm *timep)
457 if( !timep )
458 throw std::logic_error("NULL time pointer passed to Date::FromTm");
460 Year = timep->tm_year + 1900;
461 Month = timep->tm_mon;
462 Day = timep->tm_mday;
463 return true;
466 bool Date::FromBBString(const std::string &str)
468 int m, d, y;
469 if( 3 == sscanf(str.c_str(), "%d/%d/%d", &d, &m, &y) ) {
470 Year = y;
471 Month = m - 1;
472 Day = d;
473 return true;
475 return false;
478 bool Date::FromYYYYMMDD(const std::string &str)
480 int m, d, y;
481 if( 3 == sscanf(str.c_str(), "%4d%2d%2d", &y, &m, &d) ) {
482 Year = y;
483 Month = m - 1;
484 Day = d;
485 return true;
487 return false;
490 std::ostream& operator<<(std::ostream &os, const Date &date)
492 ios_format_state state(os);
494 os.setf(ios::right);
495 os.fill('0');
497 os << setw(4) << dec << date.Year << '/'
498 << setw(2) << dec << (date.Month + 1) << '/'
499 << setw(2) << dec << date.Day;
501 return os;
505 ///////////////////////////////////////////////////////////////////////////////
506 // CategoryList class
508 /// Parses the given comma delimited category string into
509 /// this CategoryList object, appending each token to the vector.
510 /// Will clear vector beforehand.
511 void CategoryList::CategoryStr2List(const std::string &str)
513 // start fresh
514 clear();
516 if( !str.size() )
517 return;
519 // parse the comma-delimited string to a list, stripping away
520 // any white space around each category name
521 string::size_type start = 0, end = 0, delim = str.find(',', start);
522 while( start != string::npos ) {
523 if( delim == string::npos )
524 end = str.size() - 1;
525 else
526 end = delim - 1;
528 // strip surrounding whitespace
529 while( str[start] == ' ' )
530 start++;
531 while( end && str[end] == ' ' )
532 end--;
534 if( start <= end ) {
535 string token = str.substr(start, end-start+1);
536 push_back(token);
539 // next
540 start = delim;
541 if( start != string::npos )
542 start++;
543 delim = str.find(',', start);
547 /// Turns the current vectory into a comma delimited category
548 /// string suitable for use in Calendar, Task, and Memo protocol values.
549 void CategoryList::CategoryList2Str(std::string &str) const
551 str.clear();
553 Barry::CategoryList::const_iterator i = begin();
554 for( ; i != end(); ++i ) {
555 if( str.size() )
556 str += ", ";
557 str += *i;
561 std::ostream& operator<<(std::ostream &os, const CategoryList &cl)
563 string buf;
564 cl.CategoryList2Str(buf);
565 os << buf;
566 return os;
569 ///////////////////////////////////////////////////////////////////////////////
570 // EnumConstants class
572 void EnumConstants::AddConstant(const char *name,
573 const std::string &display,
574 int val)
576 m_constants.push_back(EnumConstant(name, display, val));
579 const EnumConstants::EnumConstant& EnumConstants::GetConstant(int value) const
581 for( EnumConstantList::const_iterator b = m_constants.begin(), e = m_constants.end();
582 b != e;
583 ++b )
585 if( b->Value == value )
586 return *b;
589 // not found in list
590 throw std::logic_error("Enum value not found in constant list");
593 const char* EnumConstants::GetName(int value) const
595 return GetConstant(value).Name;
598 const std::string& EnumConstants::GetDisplayName(int value) const
600 return GetConstant(value).DisplayName;
603 bool EnumConstants::IsConstantValid(int value) const
605 for( EnumConstantList::const_iterator b = m_constants.begin(), e = m_constants.end();
606 b != e;
607 ++b )
609 if( b->Value == value )
610 return true;
613 // not found in list, so not a valid constant
614 return false;
618 //////////////////////////////////////////////////////////////////////////////
619 // DBNamedFieldCmp class
621 DBNamedFieldCmp::DBNamedFieldCmp(const std::string &field_name,
622 const Barry::IConverter *ic)
623 : m_name(field_name)
624 , m_ic(ic)
628 bool DBNamedFieldCmp::operator() (const Barry::DBData &a,
629 const Barry::DBData &b) const
631 #undef HANDLE_PARSER
632 #define HANDLE_PARSER(tname) \
633 else if( tname::GetDBName() == a.GetDBName() ) { \
634 tname rec1, rec2; \
635 ParseDBData(a, rec1, m_ic); \
636 ParseDBData(b, rec2, m_ic); \
637 return NamedFieldCmp<tname>(m_name).operator()(rec1, rec2); \
640 if( a.GetDBName() != b.GetDBName() ) {
641 throw logic_error("Different database types in DBNamedFieldCmp");
643 // fall through and use else's
644 ALL_KNOWN_PARSER_TYPES
646 throw logic_error("Unknown database in DBNamedFieldCmp::operator()");
649 } // namespace Barry
652 #ifdef __TEST_MODE__
654 #include <iostream>
656 int main(int argc, char *argv[])
658 if( argc < 2 ) {
659 cerr << "Usage: test <datafile>" << endl;
660 return 1;
663 std::vector<Data> array;
664 if( !LoadDataArray(argv[1], array) ) {
665 cerr << "Unable to load file: " << argv[1] << endl;
666 return 1;
669 cout << "Loaded " << array.size() << " items" << endl;
671 for( std::vector<Data>::iterator b = array.begin(), e = array.end();
672 b != e; b++ )
674 Data &d = *b;
675 // cout << d << endl;
676 if( d.GetSize() > 13 && d.GetData()[6] == 0x4f ) {
677 Barry::Contact contact;
678 size_t size = 13;
679 contact.ParseFields(d, size);
680 cout << contact << endl;
681 contact.DumpLdif(cout, "ou=People,dc=example,dc=com");
683 else if( d.GetSize() > 13 && d.GetData()[6] == 0x44 ) {
684 Barry::Calendar cal;
685 size_t size = 13;
686 cal.ParseFields(d, size);
687 cout << cal << endl;
692 #endif