lib: changed Usb::Error exception to deal in libusb_errcode instead of system
[barry/progweb.git] / src / record.cc
blob778059496b80c8d1243813b0c2ec3abb004c575e
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-2011, 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 <sstream>
34 #include <iomanip>
35 #include <time.h>
36 #include <string.h>
37 #include <stdio.h> // for sscanf()
38 #include <stdexcept>
40 #define __DEBUG_MODE__
41 #include "debug.h"
43 using namespace std;
44 using namespace Barry::Protocol;
46 namespace Barry {
48 BXEXPORT std::ostream& operator<< (std::ostream &os, const Cr2LfWrapper &str)
50 for( std::string::const_iterator i = str.m_str.begin();
51 i != str.m_str.end() && *i;
52 i++)
54 if( *i == '\r' )
55 os << '\n';
56 else
57 os << *i;
59 return os;
62 //////////////////////////////////////////////////////////////////////////////
63 // Field builder helper functions
65 void BuildField1900(Data &data, size_t &size, uint8_t type, time_t t)
67 size_t timesize = COMMON_FIELD_MIN1900_SIZE;
68 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + timesize;
69 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
70 CommonField *field = (CommonField *) pd;
72 field->size = htobs(timesize);
73 field->type = type;
74 field->u.min1900 = time2min(t);
76 size += fieldsize;
79 void BuildField(Data &data, size_t &size, uint8_t type, char c)
81 BuildField(data, size, type, (uint8_t)c);
84 void BuildField(Data &data, size_t &size, uint8_t type, uint8_t c)
86 size_t strsize = 1;
87 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
88 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
89 CommonField *field = (CommonField *) pd;
91 field->size = htobs(strsize);
92 field->type = type;
93 memcpy(field->u.raw, &c, strsize);
95 size += fieldsize;
98 void BuildField(Data &data, size_t &size, uint8_t type, uint16_t value)
100 size_t strsize = 2;
101 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
102 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
103 CommonField *field = (CommonField *) pd;
105 field->size = htobs(strsize);
106 field->type = type;
108 uint16_t store = htobs(value);
109 memcpy(field->u.raw, &store, strsize);
111 size += fieldsize;
114 void BuildField(Data &data, size_t &size, uint8_t type, uint32_t value)
116 size_t strsize = 4;
117 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
118 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
119 CommonField *field = (CommonField *) pd;
121 field->size = htobl(strsize);
122 field->type = type;
124 uint32_t store = htobl(value);
125 memcpy(field->u.raw, &store, strsize);
127 size += fieldsize;
130 void BuildField(Data &data, size_t &size, uint8_t type, uint64_t value)
132 size_t strsize = 8;
133 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
134 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
135 CommonField *field = (CommonField *) pd;
137 field->size = htobl(strsize);
138 field->type = type;
140 uint64_t store = htobll(value);
141 memcpy(field->u.raw, &store, strsize);
143 size += fieldsize;
146 void BuildField(Data &data, size_t &size, uint8_t type, const std::string &str)
148 // include null terminator
149 BuildField(data, size, type, str.c_str(), str.size() + 1);
152 void BuildField(Data &data, size_t &size, uint8_t type,
153 const void *buf, size_t bufsize)
155 // include null terminator
156 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + bufsize;
157 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
158 CommonField *field = (CommonField *) pd;
160 field->size = htobs(bufsize);
161 field->type = type;
162 memcpy(field->u.raw, buf, bufsize);
164 size += fieldsize;
167 void BuildField(Data &data, size_t &size, const Barry::UnknownField &field)
169 BuildField(data, size, field.type,
170 field.data.raw_data.data(), field.data.raw_data.size());
173 void BuildField(Data &data, size_t &size, uint8_t type, const Barry::Protocol::GroupLink &link)
175 size_t linksize = sizeof(Barry::Protocol::GroupLink);
176 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + linksize;
177 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
178 CommonField *field = (CommonField *) pd;
180 field->size = htobs(linksize);
181 field->type = type;
182 field->u.link = link;
184 size += fieldsize;
187 std::string ParseFieldString(const Barry::Protocol::CommonField *field)
189 // make no assumptions here, and pass the full size in as
190 // the maxlen, even though 99% of the time, it will be a null...
191 // this function can be used by non-null terminated strings as well
192 return ParseFieldString(field->u.raw, btohs(field->size));
195 std::string ParseFieldString(const void *data, uint16_t maxlen)
197 const char *str = (const char *)data;
199 // find last non-null character, since some fields
200 // can have multiple null terminators
201 while( maxlen && str[maxlen-1] == 0 )
202 maxlen--;
204 return std::string(str, maxlen);
208 ///////////////////////////////////////////////////////////////////////////////
209 // UnknownField
211 std::ostream& operator<< (std::ostream &os, const std::vector<UnknownField> &unknowns)
213 ios_format_state state(os);
215 std::vector<UnknownField>::const_iterator
216 ub = unknowns.begin(), ue = unknowns.end();
217 if( ub != ue )
218 os << " Unknowns:\n";
219 for( ; ub != ue; ub++ ) {
220 os << " Type: 0x" << setbase(16)
221 << (unsigned int) ub->type
222 << " Data:\n" << Data(ub->data.data(), ub->data.size());
224 return os;
228 ///////////////////////////////////////////////////////////////////////////////
229 // EmailAddress class
231 EmailAddress::EmailAddress(const std::string &complex_address)
233 size_t end = complex_address.rfind('>');
234 size_t start = complex_address.rfind('<');
235 if( start == string::npos || end == string::npos || start > end ) {
236 // simple address, add it
237 Email = complex_address;
238 Inplace::trim(Email);
240 else {
241 Name = complex_address.substr(0, start);
242 Inplace::trim(Name);
244 Email = complex_address.substr(start+1, end - start - 1);
245 Inplace::trim(Email);
249 std::ostream& operator<<(std::ostream &os, const EmailAddress &msga)
251 if( msga.Name.size() )
252 os << msga.Name << " <";
253 os << msga.Email;
254 if( msga.Name.size() )
255 os << ">";
256 return os;
260 ///////////////////////////////////////////////////////////////////////////////
261 // EmailAddressList class
263 std::string EmailAddressList::ToCommaSeparated() const
265 std::ostringstream oss;
266 oss << *this;
267 return oss.str();
270 /// Adds every email address found in the comma separated list.
271 /// Does not clear() first.
272 void EmailAddressList::AddCommaSeparated(const std::string &list)
274 istringstream iss(list);
275 string address;
276 iss >> ws;
278 while( getline(iss, address, ',') ) {
279 // trim any trailing whitespace in the address
280 size_t len = address.size();
281 while( len && ::isspace(address[len-1]) )
282 address.resize(len-1);
284 // add to list if anything left
285 if( address.size() ) {
286 EmailAddress ea(address);
287 push_back(ea);
292 std::ostream& operator<<(std::ostream &os, const EmailAddressList &elist)
294 for( EmailAddressList::const_iterator i = elist.begin(); i != elist.end(); ++i ) {
295 if( i != elist.begin() )
296 os << ", ";
297 os << *i;
299 return os;
303 ///////////////////////////////////////////////////////////////////////////////
304 // PostalAddress class
307 // GetLabel
309 /// Format a mailing address into a single string, handling missing fields.
311 std::string PostalAddress::GetLabel() const
313 std::string address = Address1;
314 if( Address2.size() ) {
315 if( address.size() )
316 address += "\n";
317 address += Address2;
319 if( Address3.size() ) {
320 if( address.size() )
321 address += "\n";
322 address += Address3;
324 if( address.size() )
325 address += "\n";
326 if( City.size() )
327 address += City + " ";
328 if( Province.size() )
329 address += Province + " ";
330 if( Country.size() )
331 address += Country;
332 if( address.size() )
333 address += "\n";
334 if( PostalCode.size() )
335 address += PostalCode;
337 return address;
340 void PostalAddress::Clear()
342 Address1.clear();
343 Address2.clear();
344 Address3.clear();
345 City.clear();
346 Province.clear();
347 PostalCode.clear();
348 Country.clear();
351 std::ostream& operator<<(std::ostream &os, const PostalAddress &post) {
352 os << post.GetLabel();
353 return os;
358 ///////////////////////////////////////////////////////////////////////////////
359 // Date class
361 Date::Date(const struct tm *timep)
363 FromTm(timep);
366 void Date::Clear()
368 Month = Day = Year = 0;
371 void Date::ToTm(struct tm *timep) const
373 memset(timep, 0, sizeof(tm));
374 timep->tm_year = Year - 1900;
375 timep->tm_mon = Month;
376 timep->tm_mday = Day;
379 std::string Date::ToYYYYMMDD() const
381 std::ostringstream oss;
382 // setfill and setw not sticky.
383 oss << setw(4) << setfill('0') << dec << Year
384 << setw(2) << setfill('0') << dec << (Month + 1)
385 << setw(2) << setfill('0') << dec << Day;
386 return oss.str();
390 // ToBBString
392 /// The Blackberry stores Birthday and Anniversary date fields
393 /// with the format: DD/MM/YYYY
395 std::string Date::ToBBString() const
397 std::ostringstream oss;
398 // setw() ain't 'sticky'!
399 oss << setw(2) << setfill('0') << dec << Day << '/'
400 << setw(2) << setfill('0') << dec << (Month + 1) << '/'
401 << setw(2) << setfill('0') << dec << Year;
402 return oss.str();
405 bool Date::FromTm(const struct tm *timep)
407 if( !timep )
408 throw std::logic_error("NULL time pointer passed to Date::FromTm");
410 Year = timep->tm_year + 1900;
411 Month = timep->tm_mon;
412 Day = timep->tm_mday;
413 return true;
416 bool Date::FromBBString(const std::string &str)
418 int m, d, y;
419 if( 3 == sscanf(str.c_str(), "%d/%d/%d", &d, &m, &y) ) {
420 Year = y;
421 Month = m - 1;
422 Day = d;
423 return true;
425 return false;
428 bool Date::FromYYYYMMDD(const std::string &str)
430 int m, d, y;
431 if( 3 == sscanf(str.c_str(), "%4d%2d%2d", &y, &m, &d) ) {
432 Year = y;
433 Month = m - 1;
434 Day = d;
435 return true;
437 return false;
440 std::ostream& operator<<(std::ostream &os, const Date &date)
442 ios_format_state state(os);
444 os.setf(ios::right);
445 os.fill('0');
447 os << setw(4) << dec << date.Year << '/'
448 << setw(2) << dec << (date.Month + 1) << '/'
449 << setw(2) << dec << date.Day;
451 return os;
455 ///////////////////////////////////////////////////////////////////////////////
456 // CategoryList class
458 /// Parses the given comma delimited category string into
459 /// this CategoryList object, appending each token to the vector.
460 /// Will clear vector beforehand.
461 void CategoryList::CategoryStr2List(const std::string &str)
463 // start fresh
464 clear();
466 if( !str.size() )
467 return;
469 // parse the comma-delimited string to a list, stripping away
470 // any white space around each category name
471 string::size_type start = 0, end = 0, delim = str.find(',', start);
472 while( start != string::npos ) {
473 if( delim == string::npos )
474 end = str.size() - 1;
475 else
476 end = delim - 1;
478 // strip surrounding whitespace
479 while( str[start] == ' ' )
480 start++;
481 while( end && str[end] == ' ' )
482 end--;
484 if( start <= end ) {
485 string token = str.substr(start, end-start+1);
486 push_back(token);
489 // next
490 start = delim;
491 if( start != string::npos )
492 start++;
493 delim = str.find(',', start);
497 /// Turns the current vectory into a comma delimited category
498 /// string suitable for use in Calendar, Task, and Memo protocol values.
499 void CategoryList::CategoryList2Str(std::string &str) const
501 str.clear();
503 Barry::CategoryList::const_iterator i = begin();
504 for( ; i != end(); ++i ) {
505 if( str.size() )
506 str += ", ";
507 str += *i;
512 } // namespace Barry
515 #ifdef __TEST_MODE__
517 #include <iostream>
519 int main(int argc, char *argv[])
521 if( argc < 2 ) {
522 cerr << "Usage: test <datafile>" << endl;
523 return 1;
526 std::vector<Data> array;
527 if( !LoadDataArray(argv[1], array) ) {
528 cerr << "Unable to load file: " << argv[1] << endl;
529 return 1;
532 cout << "Loaded " << array.size() << " items" << endl;
534 for( std::vector<Data>::iterator b = array.begin(), e = array.end();
535 b != e; b++ )
537 Data &d = *b;
538 // cout << d << endl;
539 if( d.GetSize() > 13 && d.GetData()[6] == 0x4f ) {
540 Barry::Contact contact;
541 size_t size = 13;
542 contact.ParseFields(d, size);
543 cout << contact << endl;
544 contact.DumpLdif(cout, "ou=People,dc=example,dc=com");
546 else if( d.GetSize() > 13 && d.GetData()[6] == 0x44 ) {
547 Barry::Calendar cal;
548 size_t size = 13;
549 cal.ParseFields(d, size);
550 cout << cal << endl;
555 #endif