3 /// Misc. Blackberry database record helper classes and functions.
4 /// Helps translate data from data packets to useful structures,
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.
26 #include "record-internal.h"
27 #include "protostructs.h"
33 #include "ios_state.h"
39 #include <stdio.h> // for sscanf()
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"
50 #include "r_message.h"
51 #include "r_servicebook.h"
53 #include "r_pin_message.h"
54 #include "r_saved_message.h"
57 #include "r_timezone.h"
58 #include "r_hhagent.h"
60 #define __DEBUG_MODE__
64 using namespace Barry::Protocol
;
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
;
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);
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
);
109 field
->u
.min1900
= time2min(t
);
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
)
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
);
128 memcpy(field
->u
.raw
, &c
, strsize
);
133 void BuildField(Data
&data
, size_t &size
, uint8_t type
, uint16_t value
)
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
);
143 uint16_t store
= htobs(value
);
144 memcpy(field
->u
.raw
, &store
, strsize
);
149 void BuildField(Data
&data
, size_t &size
, uint8_t type
, uint32_t value
)
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
);
159 uint32_t store
= htobl(value
);
160 memcpy(field
->u
.raw
, &store
, strsize
);
165 void BuildField(Data
&data
, size_t &size
, uint8_t type
, uint64_t value
)
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
);
175 uint64_t store
= htobll(value
);
176 memcpy(field
->u
.raw
, &store
, strsize
);
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
);
197 memcpy(field
->u
.raw
, buf
, bufsize
);
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
);
217 field
->u
.link
= link
;
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 )
239 return std::string(str
, maxlen
);
243 ///////////////////////////////////////////////////////////////////////////////
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();
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());
263 ///////////////////////////////////////////////////////////////////////////////
266 std::ostream
& operator<< (std::ostream
&os
, const EmailList
&list
)
268 for( EmailList::const_iterator b
= list
.begin(), e
= list
.end();
272 if( b
!= list
.begin() )
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
);
292 Name
= complex_address
.substr(0, start
);
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
<< " <";
305 if( msga
.Name
.size() )
311 ///////////////////////////////////////////////////////////////////////////////
312 // EmailAddressList class
314 std::string
EmailAddressList::ToCommaSeparated() const
316 std::ostringstream oss
;
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
);
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
);
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() )
354 ///////////////////////////////////////////////////////////////////////////////
355 // PostalAddress class
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() ) {
370 if( Address3
.size() ) {
378 address
+= City
+ " ";
379 if( Province
.size() )
380 address
+= Province
+ " ";
385 if( PostalCode
.size() )
386 address
+= PostalCode
;
391 void PostalAddress::Clear()
402 std::ostream
& operator<<(std::ostream
&os
, const PostalAddress
&post
) {
403 os
<< post
.GetLabel();
409 ///////////////////////////////////////////////////////////////////////////////
412 Date::Date(const struct tm
*timep
)
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
;
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
;
456 bool Date::FromTm(const struct tm
*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
;
467 bool Date::FromBBString(const std::string
&str
)
470 if( 3 == sscanf(str
.c_str(), "%d/%d/%d", &d
, &m
, &y
) ) {
479 bool Date::FromYYYYMMDD(const std::string
&str
)
482 if( 3 == sscanf(str
.c_str(), "%4d%2d%2d", &y
, &m
, &d
) ) {
491 std::ostream
& operator<<(std::ostream
&os
, const Date
&date
)
493 ios_format_state
state(os
);
498 os
<< setw(4) << dec
<< date
.Year
<< '/'
499 << setw(2) << dec
<< (date
.Month
+ 1) << '/'
500 << setw(2) << dec
<< date
.Day
;
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
)
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;
529 // strip surrounding whitespace
530 while( str
[start
] == ' ' )
532 while( end
&& str
[end
] == ' ' )
536 string token
= str
.substr(start
, end
-start
+1);
542 if( start
!= string::npos
)
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
554 Barry::CategoryList::const_iterator i
= begin();
555 for( ; i
!= end(); ++i
) {
562 std::ostream
& operator<<(std::ostream
&os
, const CategoryList
&cl
)
565 cl
.CategoryList2Str(buf
);
570 ///////////////////////////////////////////////////////////////////////////////
571 // EnumConstants class
573 void EnumConstants::AddConstant(const char *name
,
574 const std::string
&display
,
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();
586 if( b
->Value
== value
)
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();
610 if( b
->Value
== value
)
614 // not found in list, so not a valid constant
619 //////////////////////////////////////////////////////////////////////////////
620 // DBNamedFieldCmp class
622 DBNamedFieldCmp::DBNamedFieldCmp(const std::string
&field_name
,
623 const Barry::IConverter
*ic
)
629 bool DBNamedFieldCmp::operator() (const Barry::DBData
&a
,
630 const Barry::DBData
&b
) const
633 #define HANDLE_PARSER(tname) \
634 else if( tname::GetDBName() == a.GetDBName() ) { \
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()"));
657 int main(int argc
, char *argv
[])
660 cerr
<< "Usage: test <datafile>" << endl
;
664 std::vector
<Data
> array
;
665 if( !LoadDataArray(argv
[1], array
) ) {
666 cerr
<< "Unable to load file: " << argv
[1] << endl
;
670 cout
<< "Loaded " << array
.size() << " items" << endl
;
672 for( std::vector
<Data
>::iterator b
= array
.begin(), e
= array
.end();
676 // cout << d << endl;
677 if( d
.GetSize() > 13 && d
.GetData()[6] == 0x4f ) {
678 Barry::Contact contact
;
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 ) {
687 cal
.ParseFields(d
, size
);