menu: added new Keywords tag to .desktop files
[barry.git] / src / record.cc
blobcc43d7196bfd09c7736fe7c9a5e6050a689c1023
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-2013, 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 "i18n.h"
25 #include "record.h"
26 #include "record-internal.h"
27 #include "protostructs.h"
28 #include "data.h"
29 #include "time.h"
30 #include "error.h"
31 #include "endian.h"
32 #include "trim.h"
33 #include "ios_state.h"
34 #include "parser.h"
35 #include <sstream>
36 #include <iomanip>
37 #include <time.h>
38 #include <string.h>
39 #include <stdio.h> // for sscanf()
40 #include <stdexcept>
42 // for DBNamedFieldCmp below
43 #include "recordtmpl.h"
44 #include "r_calendar.h"
45 #include "r_calllog.h"
46 #include "r_bookmark.h"
47 #include "r_contact.h"
48 #include "r_cstore.h"
49 #include "r_memo.h"
50 #include "r_message.h"
51 #include "r_servicebook.h"
52 #include "r_task.h"
53 #include "r_pin_message.h"
54 #include "r_saved_message.h"
55 #include "r_sms.h"
56 #include "r_folder.h"
57 #include "r_timezone.h"
58 #include "r_hhagent.h"
60 #define __DEBUG_MODE__
61 #include "debug.h"
63 using namespace std;
64 using namespace Barry::Protocol;
66 namespace Barry {
68 std::ostream& operator<< (std::ostream &os, const Cr2LfWrapper &str)
70 for( std::string::const_iterator i = str.m_str.begin();
71 i != str.m_str.end() && *i;
72 i++)
74 if( *i == '\r' )
75 os << '\n';
76 else
77 os << *i;
79 return os;
82 std::ostream& operator<< (std::ostream &os, const TimeT &t)
84 // strip the trailing newline
85 string output = ctime(&t.Time);
86 while( output.size() &&
87 (output[output.size()-1] == '\n' ||
88 output[output.size()-1] == '\r') )
90 output.resize(output.size() - 1);
93 os << output;
94 return os;
97 //////////////////////////////////////////////////////////////////////////////
98 // Field builder helper functions
100 void BuildField1900(Data &data, size_t &size, uint8_t type, time_t t)
102 size_t timesize = COMMON_FIELD_MIN1900_SIZE;
103 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + timesize;
104 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
105 CommonField *field = (CommonField *) pd;
107 field->size = htobs(timesize);
108 field->type = type;
109 field->u.min1900 = time2min(t);
111 size += fieldsize;
114 void BuildField(Data &data, size_t &size, uint8_t type, char c)
116 BuildField(data, size, type, (uint8_t)c);
119 void BuildField(Data &data, size_t &size, uint8_t type, uint8_t c)
121 size_t strsize = 1;
122 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
123 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
124 CommonField *field = (CommonField *) pd;
126 field->size = htobs(strsize);
127 field->type = type;
128 memcpy(field->u.raw, &c, strsize);
130 size += fieldsize;
133 void BuildField(Data &data, size_t &size, uint8_t type, uint16_t value)
135 size_t strsize = 2;
136 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
137 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
138 CommonField *field = (CommonField *) pd;
140 field->size = htobs(strsize);
141 field->type = type;
143 uint16_t store = htobs(value);
144 memcpy(field->u.raw, &store, strsize);
146 size += fieldsize;
149 void BuildField(Data &data, size_t &size, uint8_t type, uint32_t value)
151 size_t strsize = 4;
152 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
153 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
154 CommonField *field = (CommonField *) pd;
156 field->size = htobl(strsize);
157 field->type = type;
159 uint32_t store = htobl(value);
160 memcpy(field->u.raw, &store, strsize);
162 size += fieldsize;
165 void BuildField(Data &data, size_t &size, uint8_t type, uint64_t value)
167 size_t strsize = 8;
168 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
169 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
170 CommonField *field = (CommonField *) pd;
172 field->size = htobl(strsize);
173 field->type = type;
175 uint64_t store = htobll(value);
176 memcpy(field->u.raw, &store, strsize);
178 size += fieldsize;
181 void BuildField(Data &data, size_t &size, uint8_t type, const std::string &str)
183 // include null terminator
184 BuildField(data, size, type, str.c_str(), str.size() + 1);
187 void BuildField(Data &data, size_t &size, uint8_t type,
188 const void *buf, size_t bufsize)
190 // include null terminator
191 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + bufsize;
192 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
193 CommonField *field = (CommonField *) pd;
195 field->size = htobs(bufsize);
196 field->type = type;
197 memcpy(field->u.raw, buf, bufsize);
199 size += fieldsize;
202 void BuildField(Data &data, size_t &size, const Barry::UnknownField &field)
204 BuildField(data, size, field.type,
205 field.data.raw_data.data(), field.data.raw_data.size());
208 void BuildField(Data &data, size_t &size, uint8_t type, const Barry::Protocol::GroupLink &link)
210 size_t linksize = sizeof(Barry::Protocol::GroupLink);
211 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + linksize;
212 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
213 CommonField *field = (CommonField *) pd;
215 field->size = htobs(linksize);
216 field->type = type;
217 field->u.link = link;
219 size += fieldsize;
222 std::string ParseFieldString(const Barry::Protocol::CommonField *field)
224 // make no assumptions here, and pass the full size in as
225 // the maxlen, even though 99% of the time, it will be a null...
226 // this function can be used by non-null terminated strings as well
227 return ParseFieldString(field->u.raw, btohs(field->size));
230 std::string ParseFieldString(const void *data, uint16_t maxlen)
232 const char *str = (const char *)data;
234 // find last non-null character, since some fields
235 // can have multiple null terminators
236 while( maxlen && str[maxlen-1] == 0 )
237 maxlen--;
239 return std::string(str, maxlen);
243 ///////////////////////////////////////////////////////////////////////////////
244 // UnknownField
246 std::ostream& operator<< (std::ostream &os, const std::vector<UnknownField> &unknowns)
248 ios_format_state state(os);
250 std::vector<UnknownField>::const_iterator
251 ub = unknowns.begin(), ue = unknowns.end();
252 if( ub != ue )
253 os << _(" Unknowns:\n");
254 for( ; ub != ue; ub++ ) {
255 os << _(" Type: ") << "0x" << setbase(16)
256 << (unsigned int) ub->type
257 << _(" Data:\n") << Data(ub->data.data(), ub->data.size());
259 return os;
263 ///////////////////////////////////////////////////////////////////////////////
264 // EmailList typedef
266 std::ostream& operator<< (std::ostream &os, const EmailList &list)
268 for( EmailList::const_iterator b = list.begin(), e = list.end();
269 b != e;
270 ++b )
272 if( b != list.begin() )
273 os << ", ";
274 os << *b;
276 return os;
279 ///////////////////////////////////////////////////////////////////////////////
280 // EmailAddress class
282 EmailAddress::EmailAddress(const std::string &complex_address)
284 size_t end = complex_address.rfind('>');
285 size_t start = complex_address.rfind('<');
286 if( start == string::npos || end == string::npos || start > end ) {
287 // simple address, add it
288 Email = complex_address;
289 Inplace::trim(Email);
291 else {
292 Name = complex_address.substr(0, start);
293 Inplace::trim(Name);
295 Email = complex_address.substr(start+1, end - start - 1);
296 Inplace::trim(Email);
300 std::ostream& operator<<(std::ostream &os, const EmailAddress &msga)
302 if( msga.Name.size() )
303 os << msga.Name << " <";
304 os << msga.Email;
305 if( msga.Name.size() )
306 os << ">";
307 return os;
311 ///////////////////////////////////////////////////////////////////////////////
312 // EmailAddressList class
314 std::string EmailAddressList::ToCommaSeparated() const
316 std::ostringstream oss;
317 oss << *this;
318 return oss.str();
321 /// Adds every email address found in the comma separated list.
322 /// Does not clear() first.
323 void EmailAddressList::AddCommaSeparated(const std::string &list)
325 istringstream iss(list);
326 string address;
327 iss >> ws;
329 while( getline(iss, address, ',') ) {
330 // trim any trailing whitespace in the address
331 size_t len = address.size();
332 while( len && ::isspace(address[len-1]) )
333 address.resize(len-1);
335 // add to list if anything left
336 if( address.size() ) {
337 EmailAddress ea(address);
338 push_back(ea);
343 std::ostream& operator<<(std::ostream &os, const EmailAddressList &elist)
345 for( EmailAddressList::const_iterator i = elist.begin(); i != elist.end(); ++i ) {
346 if( i != elist.begin() )
347 os << ", ";
348 os << *i;
350 return os;
354 ///////////////////////////////////////////////////////////////////////////////
355 // PostalAddress class
358 // GetLabel
360 /// Format a mailing address into a single string, handling missing fields.
362 std::string PostalAddress::GetLabel() const
364 std::string address = Address1;
365 if( Address2.size() ) {
366 if( address.size() )
367 address += "\n";
368 address += Address2;
370 if( Address3.size() ) {
371 if( address.size() )
372 address += "\n";
373 address += Address3;
375 if( address.size() )
376 address += "\n";
377 if( City.size() )
378 address += City + " ";
379 if( Province.size() )
380 address += Province + " ";
381 if( Country.size() )
382 address += Country;
383 if( address.size() )
384 address += "\n";
385 if( PostalCode.size() )
386 address += PostalCode;
388 return address;
391 void PostalAddress::Clear()
393 Address1.clear();
394 Address2.clear();
395 Address3.clear();
396 City.clear();
397 Province.clear();
398 PostalCode.clear();
399 Country.clear();
402 std::ostream& operator<<(std::ostream &os, const PostalAddress &post) {
403 os << post.GetLabel();
404 return os;
409 ///////////////////////////////////////////////////////////////////////////////
410 // Date class
412 Date::Date(const struct tm *timep)
414 FromTm(timep);
417 void Date::Clear()
419 Month = Day = Year = 0;
422 void Date::ToTm(struct tm *timep) const
424 memset(timep, 0, sizeof(tm));
425 timep->tm_year = Year - 1900;
426 timep->tm_mon = Month;
427 timep->tm_mday = Day;
430 std::string Date::ToYYYYMMDD() const
432 std::ostringstream oss;
433 // setfill and setw not sticky.
434 oss << setw(4) << setfill('0') << dec << Year
435 << setw(2) << setfill('0') << dec << (Month + 1)
436 << setw(2) << setfill('0') << dec << Day;
437 return oss.str();
441 // ToBBString
443 /// The Blackberry stores Birthday and Anniversary date fields
444 /// with the format: DD/MM/YYYY
446 std::string Date::ToBBString() const
448 std::ostringstream oss;
449 // setw() ain't 'sticky'!
450 oss << setw(2) << setfill('0') << dec << Day << '/'
451 << setw(2) << setfill('0') << dec << (Month + 1) << '/'
452 << setw(2) << setfill('0') << dec << Year;
453 return oss.str();
456 bool Date::FromTm(const struct tm *timep)
458 if( !timep )
459 throw std::logic_error(_("NULL time pointer passed to Date::FromTm"));
461 Year = timep->tm_year + 1900;
462 Month = timep->tm_mon;
463 Day = timep->tm_mday;
464 return true;
467 bool Date::FromBBString(const std::string &str)
469 int m, d, y;
470 if( 3 == sscanf(str.c_str(), "%d/%d/%d", &d, &m, &y) ) {
471 Year = y;
472 Month = m - 1;
473 Day = d;
474 return true;
476 return false;
479 bool Date::FromYYYYMMDD(const std::string &str)
481 int m, d, y;
482 if( 3 == sscanf(str.c_str(), "%4d%2d%2d", &y, &m, &d) ) {
483 Year = y;
484 Month = m - 1;
485 Day = d;
486 return true;
488 return false;
491 std::ostream& operator<<(std::ostream &os, const Date &date)
493 ios_format_state state(os);
495 os.setf(ios::right);
496 os.fill('0');
498 os << setw(4) << dec << date.Year << '/'
499 << setw(2) << dec << (date.Month + 1) << '/'
500 << setw(2) << dec << date.Day;
502 return os;
506 ///////////////////////////////////////////////////////////////////////////////
507 // CategoryList class
509 /// Parses the given comma delimited category string into
510 /// this CategoryList object, appending each token to the vector.
511 /// Will clear vector beforehand.
512 void CategoryList::CategoryStr2List(const std::string &str)
514 // start fresh
515 clear();
517 if( !str.size() )
518 return;
520 // parse the comma-delimited string to a list, stripping away
521 // any white space around each category name
522 string::size_type start = 0, end = 0, delim = str.find(',', start);
523 while( start != string::npos ) {
524 if( delim == string::npos )
525 end = str.size() - 1;
526 else
527 end = delim - 1;
529 // strip surrounding whitespace
530 while( str[start] == ' ' )
531 start++;
532 while( end && str[end] == ' ' )
533 end--;
535 if( start <= end ) {
536 string token = str.substr(start, end-start+1);
537 push_back(token);
540 // next
541 start = delim;
542 if( start != string::npos )
543 start++;
544 delim = str.find(',', start);
548 /// Turns the current vectory into a comma delimited category
549 /// string suitable for use in Calendar, Task, and Memo protocol values.
550 void CategoryList::CategoryList2Str(std::string &str) const
552 str.clear();
554 Barry::CategoryList::const_iterator i = begin();
555 for( ; i != end(); ++i ) {
556 if( str.size() )
557 str += ", ";
558 str += *i;
562 std::ostream& operator<<(std::ostream &os, const CategoryList &cl)
564 string buf;
565 cl.CategoryList2Str(buf);
566 os << buf;
567 return os;
570 ///////////////////////////////////////////////////////////////////////////////
571 // EnumConstants class
573 void EnumConstants::AddConstant(const char *name,
574 const std::string &display,
575 int val)
577 m_constants.push_back(EnumConstant(name, display, val));
580 const EnumConstants::EnumConstant& EnumConstants::GetConstant(int value) const
582 for( EnumConstantList::const_iterator b = m_constants.begin(), e = m_constants.end();
583 b != e;
584 ++b )
586 if( b->Value == value )
587 return *b;
590 // not found in list
591 throw std::logic_error(_("Enum value not found in constant list"));
594 const char* EnumConstants::GetName(int value) const
596 return GetConstant(value).Name;
599 const std::string& EnumConstants::GetDisplayName(int value) const
601 return GetConstant(value).DisplayName;
604 bool EnumConstants::IsConstantValid(int value) const
606 for( EnumConstantList::const_iterator b = m_constants.begin(), e = m_constants.end();
607 b != e;
608 ++b )
610 if( b->Value == value )
611 return true;
614 // not found in list, so not a valid constant
615 return false;
619 //////////////////////////////////////////////////////////////////////////////
620 // DBNamedFieldCmp class
622 DBNamedFieldCmp::DBNamedFieldCmp(const std::string &field_name,
623 const Barry::IConverter *ic)
624 : m_name(field_name)
625 , m_ic(ic)
629 bool DBNamedFieldCmp::operator() (const Barry::DBData &a,
630 const Barry::DBData &b) const
632 #undef HANDLE_PARSER
633 #define HANDLE_PARSER(tname) \
634 else if( tname::GetDBName() == a.GetDBName() ) { \
635 tname rec1, rec2; \
636 ParseDBData(a, rec1, m_ic); \
637 ParseDBData(b, rec2, m_ic); \
638 return NamedFieldCmp<tname>(m_name).operator()(rec1, rec2); \
641 if( a.GetDBName() != b.GetDBName() ) {
642 throw logic_error(_("Different database types in DBNamedFieldCmp"));
644 // fall through and use else's
645 ALL_KNOWN_PARSER_TYPES
647 throw logic_error(_("Unknown database in DBNamedFieldCmp::operator()"));
650 } // namespace Barry
653 #ifdef __TEST_MODE__
655 #include <iostream>
657 int main(int argc, char *argv[])
659 if( argc < 2 ) {
660 cerr << "Usage: test <datafile>" << endl;
661 return 1;
664 std::vector<Data> array;
665 if( !LoadDataArray(argv[1], array) ) {
666 cerr << "Unable to load file: " << argv[1] << endl;
667 return 1;
670 cout << "Loaded " << array.size() << " items" << endl;
672 for( std::vector<Data>::iterator b = array.begin(), e = array.end();
673 b != e; b++ )
675 Data &d = *b;
676 // cout << d << endl;
677 if( d.GetSize() > 13 && d.GetData()[6] == 0x4f ) {
678 Barry::Contact contact;
679 size_t size = 13;
680 contact.ParseFields(d, size);
681 cout << contact << endl;
682 contact.DumpLdif(cout, "ou=People,dc=example,dc=com");
684 else if( d.GetSize() > 13 && d.GetData()[6] == 0x44 ) {
685 Barry::Calendar cal;
686 size_t size = 13;
687 cal.ParseFields(d, size);
688 cout << cal << endl;
693 #endif