lib: created consistent method of printing strings with CR characters
[barry.git] / src / record.cc
blobdb415a64e325035ffdc5a2bbfc7c1a93d2dc45fe
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-2010, 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 "protocol.h"
27 #include "protostructs.h"
28 #include "data.h"
29 #include "time.h"
30 #include "error.h"
31 #include "endian.h"
32 #include <sstream>
33 #include <iomanip>
34 #include <time.h>
35 #include <string.h>
36 #include <stdio.h> // for sscanf()
37 #include <stdexcept>
39 #define __DEBUG_MODE__
40 #include "debug.h"
42 using namespace std;
43 using namespace Barry::Protocol;
45 namespace Barry {
47 BXEXPORT std::ostream& operator<< (std::ostream &os, const Cr2LfWrapper &str)
49 for( std::string::const_iterator i = str.m_str.begin();
50 i != str.m_str.end() && *i;
51 i++)
53 if( *i == '\r' )
54 os << '\n';
55 else
56 os << *i;
58 return os;
61 //////////////////////////////////////////////////////////////////////////////
62 // Field builder helper functions
64 void BuildField1900(Data &data, size_t &size, uint8_t type, time_t t)
66 size_t timesize = COMMON_FIELD_MIN1900_SIZE;
67 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + timesize;
68 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
69 CommonField *field = (CommonField *) pd;
71 field->size = htobs(timesize);
72 field->type = type;
73 field->u.min1900 = time2min(t);
75 size += fieldsize;
78 void BuildField(Data &data, size_t &size, uint8_t type, char c)
80 BuildField(data, size, type, (uint8_t)c);
83 void BuildField(Data &data, size_t &size, uint8_t type, uint8_t c)
85 size_t strsize = 1;
86 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
87 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
88 CommonField *field = (CommonField *) pd;
90 field->size = htobs(strsize);
91 field->type = type;
92 memcpy(field->u.raw, &c, strsize);
94 size += fieldsize;
97 void BuildField(Data &data, size_t &size, uint8_t type, uint16_t value)
99 size_t strsize = 2;
100 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
101 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
102 CommonField *field = (CommonField *) pd;
104 field->size = htobs(strsize);
105 field->type = type;
107 uint16_t store = htobs(value);
108 memcpy(field->u.raw, &store, strsize);
110 size += fieldsize;
113 void BuildField(Data &data, size_t &size, uint8_t type, uint32_t value)
115 size_t strsize = 4;
116 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
117 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
118 CommonField *field = (CommonField *) pd;
120 field->size = htobl(strsize);
121 field->type = type;
123 uint32_t store = htobl(value);
124 memcpy(field->u.raw, &store, strsize);
126 size += fieldsize;
129 void BuildField(Data &data, size_t &size, uint8_t type, uint64_t value)
131 size_t strsize = 8;
132 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
133 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
134 CommonField *field = (CommonField *) pd;
136 field->size = htobl(strsize);
137 field->type = type;
139 uint64_t store = htobll(value);
140 memcpy(field->u.raw, &store, strsize);
142 size += fieldsize;
145 void BuildField(Data &data, size_t &size, uint8_t type, const std::string &str)
147 // include null terminator
148 BuildField(data, size, type, str.c_str(), str.size() + 1);
151 void BuildField(Data &data, size_t &size, uint8_t type,
152 const void *buf, size_t bufsize)
154 // include null terminator
155 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + bufsize;
156 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
157 CommonField *field = (CommonField *) pd;
159 field->size = htobs(bufsize);
160 field->type = type;
161 memcpy(field->u.raw, buf, bufsize);
163 size += fieldsize;
166 void BuildField(Data &data, size_t &size, const Barry::UnknownField &field)
168 BuildField(data, size, field.type,
169 field.data.raw_data.data(), field.data.raw_data.size());
172 void BuildField(Data &data, size_t &size, uint8_t type, const Barry::Protocol::GroupLink &link)
174 size_t linksize = sizeof(Barry::Protocol::GroupLink);
175 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + linksize;
176 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
177 CommonField *field = (CommonField *) pd;
179 field->size = htobs(linksize);
180 field->type = type;
181 field->u.link = link;
183 size += fieldsize;
186 std::string ParseFieldString(const Barry::Protocol::CommonField *field)
188 // make no assumptions here, and pass the full size in as
189 // the maxlen, even though 99% of the time, it will be a null...
190 // this function can be used by non-null terminated strings as well
191 return ParseFieldString(field->u.raw, btohs(field->size));
194 std::string ParseFieldString(const void *data, uint16_t maxlen)
196 const char *str = (const char *)data;
198 // find last non-null character, since some fields
199 // can have multiple null terminators
200 while( maxlen && str[maxlen-1] == 0 )
201 maxlen--;
203 return std::string(str, maxlen);
207 ///////////////////////////////////////////////////////////////////////////////
208 // UnknownField
210 std::ostream& operator<< (std::ostream &os, const std::vector<UnknownField> &unknowns)
212 std::vector<UnknownField>::const_iterator
213 ub = unknowns.begin(), ue = unknowns.end();
214 if( ub != ue )
215 os << " Unknowns:\n";
216 for( ; ub != ue; ub++ ) {
217 os << " Type: 0x" << setbase(16)
218 << (unsigned int) ub->type
219 << " Data:\n" << Data(ub->data.data(), ub->data.size());
221 return os;
225 ///////////////////////////////////////////////////////////////////////////////
226 // EmailAddress class
228 std::ostream& operator<<(std::ostream &os, const EmailAddress &msga) {
229 os << msga.Name << " <" << msga.Email << ">";
230 return os;
233 std::ostream& operator<<(std::ostream &os, const EmailAddressList &elist) {
234 for( EmailAddressList::const_iterator i = elist.begin(); i != elist.end(); ++i ) {
235 if( i != elist.begin() )
236 os << ", ";
237 os << *i;
239 return os;
243 ///////////////////////////////////////////////////////////////////////////////
244 // PostalAddress class
247 // GetLabel
249 /// Format a mailing address into a single string, handling missing fields.
251 std::string PostalAddress::GetLabel() const
253 std::string address = Address1;
254 if( Address2.size() ) {
255 if( address.size() )
256 address += "\n";
257 address += Address2;
259 if( Address3.size() ) {
260 if( address.size() )
261 address += "\n";
262 address += Address3;
264 if( address.size() )
265 address += "\n";
266 if( City.size() )
267 address += City + " ";
268 if( Province.size() )
269 address += Province + " ";
270 if( Country.size() )
271 address += Country;
272 if( address.size() )
273 address += "\n";
274 if( PostalCode.size() )
275 address += PostalCode;
277 return address;
280 void PostalAddress::Clear()
282 Address1.clear();
283 Address2.clear();
284 Address3.clear();
285 City.clear();
286 Province.clear();
287 PostalCode.clear();
288 Country.clear();
291 std::ostream& operator<<(std::ostream &os, const PostalAddress &post) {
292 os << post.GetLabel();
293 return os;
298 ///////////////////////////////////////////////////////////////////////////////
299 // Date class
301 Date::Date(const struct tm *timep)
303 FromTm(timep);
306 void Date::Clear()
308 Month = Day = Year = 0;
311 void Date::ToTm(struct tm *timep) const
313 memset(timep, 0, sizeof(tm));
314 timep->tm_year = Year - 1900;
315 timep->tm_mon = Month;
316 timep->tm_mday = Day;
319 std::string Date::ToYYYYMMDD() const
321 std::ostringstream oss;
322 // setfill and setw not sticky.
323 oss << setw(4) << setfill('0') << Year
324 << setw(2) << setfill('0') << (Month + 1)
325 << setw(2) << setfill('0') << Day;
326 return oss.str();
330 // ToBBString
332 /// The Blackberry stores Birthday and Anniversary date fields
333 /// with the format: DD/MM/YYYY
335 std::string Date::ToBBString() const
337 std::ostringstream oss;
338 // setw() ain't 'sticky'!
339 oss << setw(2) << setfill('0') << Day << '/'
340 << setw(2) << setfill('0') << (Month + 1) << '/'
341 << setw(2) << setfill('0') << Year;
342 return oss.str();
345 bool Date::FromTm(const struct tm *timep)
347 if( !timep )
348 throw std::logic_error("NULL time pointer passed to Date::FromTm");
350 Year = timep->tm_year + 1900;
351 Month = timep->tm_mon;
352 Day = timep->tm_mday;
353 return true;
356 bool Date::FromBBString(const std::string &str)
358 int m, d, y;
359 if( 3 == sscanf(str.c_str(), "%d/%d/%d", &d, &m, &y) ) {
360 Year = y;
361 Month = m - 1;
362 Day = d;
363 return true;
365 return false;
368 bool Date::FromYYYYMMDD(const std::string &str)
370 int m, d, y;
371 if( 3 == sscanf(str.c_str(), "%4d%2d%2d", &y, &m, &d) ) {
372 Year = y;
373 Month = m - 1;
374 Day = d;
375 return true;
377 return false;
380 std::ostream& operator<<(std::ostream &os, const Date &date)
382 os << setw(4) << dec << date.Year << '/'
383 << setw(2) << dec << (date.Month + 1) << '/'
384 << setw(2) << dec << date.Day;
385 return os;
389 ///////////////////////////////////////////////////////////////////////////////
390 // CategoryList class
392 /// Parses the given comma delimited category string into
393 /// this CategoryList object, appending each token to the vector.
394 /// Will clear vector beforehand.
395 void CategoryList::CategoryStr2List(const std::string &str)
397 // start fresh
398 clear();
400 if( !str.size() )
401 return;
403 // parse the comma-delimited string to a list, stripping away
404 // any white space around each category name
405 string::size_type start = 0, end = 0, delim = str.find(',', start);
406 while( start != string::npos ) {
407 if( delim == string::npos )
408 end = str.size() - 1;
409 else
410 end = delim - 1;
412 // strip surrounding whitespace
413 while( str[start] == ' ' )
414 start++;
415 while( end && str[end] == ' ' )
416 end--;
418 if( start <= end ) {
419 string token = str.substr(start, end-start+1);
420 push_back(token);
423 // next
424 start = delim;
425 if( start != string::npos )
426 start++;
427 delim = str.find(',', start);
431 /// Turns the current vectory into a comma delimited category
432 /// string suitable for use in Calendar, Task, and Memo protocol values.
433 void CategoryList::CategoryList2Str(std::string &str) const
435 str.clear();
437 Barry::CategoryList::const_iterator i = begin();
438 for( ; i != end(); ++i ) {
439 if( str.size() )
440 str += ", ";
441 str += *i;
446 } // namespace Barry
449 #ifdef __TEST_MODE__
451 #include <iostream>
453 int main(int argc, char *argv[])
455 if( argc < 2 ) {
456 cerr << "Usage: test <datafile>" << endl;
457 return 1;
460 std::vector<Data> array;
461 if( !LoadDataArray(argv[1], array) ) {
462 cerr << "Unable to load file: " << argv[1] << endl;
463 return 1;
466 cout << "Loaded " << array.size() << " items" << endl;
468 for( std::vector<Data>::iterator b = array.begin(), e = array.end();
469 b != e; b++ )
471 Data &d = *b;
472 // cout << d << endl;
473 if( d.GetSize() > 13 && d.GetData()[6] == 0x4f ) {
474 Barry::Contact contact;
475 size_t size = 13;
476 contact.ParseFields(d, size);
477 cout << contact << endl;
478 contact.DumpLdif(cout, "ou=People,dc=example,dc=com");
480 else if( d.GetSize() > 13 && d.GetData()[6] == 0x44 ) {
481 Barry::Calendar cal;
482 size_t size = 13;
483 cal.ParseFields(d, size);
484 cout << cal << endl;
489 #endif