lib: show offset and rectype in HexDumpParser
[barry.git] / src / record.cc
blobc9634b754d0291213bbd190dfabb86ea130ee1fc
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 //////////////////////////////////////////////////////////////////////////////
48 // Field builder helper functions
50 void BuildField1900(Data &data, size_t &size, uint8_t type, time_t t)
52 size_t timesize = COMMON_FIELD_MIN1900_SIZE;
53 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + timesize;
54 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
55 CommonField *field = (CommonField *) pd;
57 field->size = htobs(timesize);
58 field->type = type;
59 field->u.min1900 = time2min(t);
61 size += fieldsize;
64 void BuildField(Data &data, size_t &size, uint8_t type, char c)
66 BuildField(data, size, type, (uint8_t)c);
69 void BuildField(Data &data, size_t &size, uint8_t type, uint8_t c)
71 size_t strsize = 1;
72 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
73 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
74 CommonField *field = (CommonField *) pd;
76 field->size = htobs(strsize);
77 field->type = type;
78 memcpy(field->u.raw, &c, strsize);
80 size += fieldsize;
83 void BuildField(Data &data, size_t &size, uint8_t type, uint16_t value)
85 size_t strsize = 2;
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;
93 uint16_t store = htobs(value);
94 memcpy(field->u.raw, &store, strsize);
96 size += fieldsize;
99 void BuildField(Data &data, size_t &size, uint8_t type, uint32_t value)
101 size_t strsize = 4;
102 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
103 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
104 CommonField *field = (CommonField *) pd;
106 field->size = htobl(strsize);
107 field->type = type;
109 uint32_t store = htobl(value);
110 memcpy(field->u.raw, &store, strsize);
112 size += fieldsize;
115 void BuildField(Data &data, size_t &size, uint8_t type, uint64_t value)
117 size_t strsize = 8;
118 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
119 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
120 CommonField *field = (CommonField *) pd;
122 field->size = htobl(strsize);
123 field->type = type;
125 uint64_t store = htobll(value);
126 memcpy(field->u.raw, &store, strsize);
128 size += fieldsize;
131 void BuildField(Data &data, size_t &size, uint8_t type, const std::string &str)
133 // include null terminator
134 BuildField(data, size, type, str.c_str(), str.size() + 1);
137 void BuildField(Data &data, size_t &size, uint8_t type,
138 const void *buf, size_t bufsize)
140 // include null terminator
141 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + bufsize;
142 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
143 CommonField *field = (CommonField *) pd;
145 field->size = htobs(bufsize);
146 field->type = type;
147 memcpy(field->u.raw, buf, bufsize);
149 size += fieldsize;
152 void BuildField(Data &data, size_t &size, const Barry::UnknownField &field)
154 BuildField(data, size, field.type,
155 field.data.raw_data.data(), field.data.raw_data.size());
158 void BuildField(Data &data, size_t &size, uint8_t type, const Barry::Protocol::GroupLink &link)
160 size_t linksize = sizeof(Barry::Protocol::GroupLink);
161 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + linksize;
162 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
163 CommonField *field = (CommonField *) pd;
165 field->size = htobs(linksize);
166 field->type = type;
167 field->u.link = link;
169 size += fieldsize;
172 std::string ParseFieldString(const Barry::Protocol::CommonField *field)
174 // make no assumptions here, and pass the full size in as
175 // the maxlen, even though 99% of the time, it will be a null...
176 // this function can be used by non-null terminated strings as well
177 return ParseFieldString(field->u.raw, btohs(field->size));
180 std::string ParseFieldString(const void *data, uint16_t maxlen)
182 const char *str = (const char *)data;
184 // find last non-null character, since some fields
185 // can have multiple null terminators
186 while( maxlen && str[maxlen-1] == 0 )
187 maxlen--;
189 return std::string(str, maxlen);
193 ///////////////////////////////////////////////////////////////////////////////
194 // UnknownField
196 std::ostream& operator<< (std::ostream &os, const std::vector<UnknownField> &unknowns)
198 std::vector<UnknownField>::const_iterator
199 ub = unknowns.begin(), ue = unknowns.end();
200 if( ub != ue )
201 os << " Unknowns:\n";
202 for( ; ub != ue; ub++ ) {
203 os << " Type: 0x" << setbase(16)
204 << (unsigned int) ub->type
205 << " Data:\n" << Data(ub->data.data(), ub->data.size());
207 return os;
211 ///////////////////////////////////////////////////////////////////////////////
212 // EmailAddress class
214 std::ostream& operator<<(std::ostream &os, const EmailAddress &msga) {
215 os << msga.Name << " <" << msga.Email << ">";
216 return os;
219 std::ostream& operator<<(std::ostream &os, const EmailAddressList &elist) {
220 for( EmailAddressList::const_iterator i = elist.begin(); i != elist.end(); ++i ) {
221 if( i != elist.begin() )
222 os << ", ";
223 os << *i;
225 return os;
229 ///////////////////////////////////////////////////////////////////////////////
230 // PostalAddress class
233 // GetLabel
235 /// Format a mailing address into a single string, handling missing fields.
237 std::string PostalAddress::GetLabel() const
239 std::string address = Address1;
240 if( Address2.size() ) {
241 if( address.size() )
242 address += "\n";
243 address += Address2;
245 if( Address3.size() ) {
246 if( address.size() )
247 address += "\n";
248 address += Address3;
250 if( address.size() )
251 address += "\n";
252 if( City.size() )
253 address += City + " ";
254 if( Province.size() )
255 address += Province + " ";
256 if( Country.size() )
257 address += Country;
258 if( address.size() )
259 address += "\n";
260 if( PostalCode.size() )
261 address += PostalCode;
263 return address;
266 void PostalAddress::Clear()
268 Address1.clear();
269 Address2.clear();
270 Address3.clear();
271 City.clear();
272 Province.clear();
273 PostalCode.clear();
274 Country.clear();
277 std::ostream& operator<<(std::ostream &os, const PostalAddress &post) {
278 os << post.GetLabel();
279 return os;
284 ///////////////////////////////////////////////////////////////////////////////
285 // Date class
287 Date::Date(const struct tm *timep)
289 FromTm(timep);
292 void Date::Clear()
294 Month = Day = Year = 0;
297 void Date::ToTm(struct tm *timep) const
299 memset(timep, 0, sizeof(tm));
300 timep->tm_year = Year - 1900;
301 timep->tm_mon = Month;
302 timep->tm_mday = Day;
305 std::string Date::ToYYYYMMDD() const
307 std::ostringstream oss;
308 // setfill and setw not sticky.
309 oss << setw(4) << setfill('0') << Year
310 << setw(2) << setfill('0') << Month + 1
311 << setw(2) << setfill('0') << Day;
312 return oss.str();
316 // ToBBString
318 /// The Blackberry stores Birthday and Anniversary date fields
319 /// with the format: DD/MM/YYYY
321 std::string Date::ToBBString() const
323 std::ostringstream oss;
324 // setw() ain't 'sticky'!
325 oss << setw(2) << setfill('0') << Day << '/'
326 << setw(2) << setfill('0') << Month + 1 << '/'
327 << setw(2) << setfill('0') << Year;
328 return oss.str();
331 bool Date::FromTm(const struct tm *timep)
333 Year = timep->tm_year + 1900;
334 Month = timep->tm_mon;
335 Day = timep->tm_mday;
336 return true;
339 bool Date::FromBBString(const std::string &str)
341 int m, d, y;
342 if( 3 == sscanf(str.c_str(), "%d/%d/%d", &d, &m, &y) ) {
343 Year = y;
344 Month = m - 1;
345 Day = d;
346 return true;
348 return false;
351 bool Date::FromYYYYMMDD(const std::string &str)
353 int m, d, y;
354 if( 3 == sscanf(str.c_str(), "%4d%2d%2d", &y, &m, &d) ) {
355 Year = y;
356 Month = m - 1;
357 Day = d;
358 return true;
360 return false;
363 std::ostream& operator<<(std::ostream &os, const Date &date)
365 os << setw(4) << date.Year << '/'
366 << setw(2) << date.Month << '/'
367 << setw(2) << date.Day;
368 return os;
372 ///////////////////////////////////////////////////////////////////////////////
373 // CategoryList class
375 /// Parses the given comma delimited category string into
376 /// this CategoryList object, appending each token to the vector.
377 /// Will clear vector beforehand.
378 void CategoryList::CategoryStr2List(const std::string &str)
380 // start fresh
381 clear();
383 if( !str.size() )
384 return;
386 // parse the comma-delimited string to a list, stripping away
387 // any white space around each category name
388 string::size_type start = 0, end = 0, delim = str.find(',', start);
389 while( start != string::npos ) {
390 if( delim == string::npos )
391 end = str.size() - 1;
392 else
393 end = delim - 1;
395 // strip surrounding whitespace
396 while( str[start] == ' ' )
397 start++;
398 while( end && str[end] == ' ' )
399 end--;
401 if( start <= end ) {
402 string token = str.substr(start, end-start+1);
403 push_back(token);
406 // next
407 start = delim;
408 if( start != string::npos )
409 start++;
410 delim = str.find(',', start);
414 /// Turns the current vectory into a comma delimited category
415 /// string suitable for use in Calendar, Task, and Memo protocol values.
416 void CategoryList::CategoryList2Str(std::string &str) const
418 str.clear();
420 Barry::CategoryList::const_iterator i = begin();
421 for( ; i != end(); ++i ) {
422 if( str.size() )
423 str += ", ";
424 str += *i;
429 } // namespace Barry
432 #ifdef __TEST_MODE__
434 #include <iostream>
436 int main(int argc, char *argv[])
438 if( argc < 2 ) {
439 cerr << "Usage: test <datafile>" << endl;
440 return 1;
443 std::vector<Data> array;
444 if( !LoadDataArray(argv[1], array) ) {
445 cerr << "Unable to load file: " << argv[1] << endl;
446 return 1;
449 cout << "Loaded " << array.size() << " items" << endl;
451 for( std::vector<Data>::iterator b = array.begin(), e = array.end();
452 b != e; b++ )
454 Data &d = *b;
455 // cout << d << endl;
456 if( d.GetSize() > 13 && d.GetData()[6] == 0x4f ) {
457 Barry::Contact contact;
458 size_t size = 13;
459 contact.ParseFields(d, size);
460 cout << contact << endl;
461 contact.DumpLdif(cout, "ou=People,dc=example,dc=com");
463 else if( d.GetSize() > 13 && d.GetData()[6] == 0x44 ) {
464 Barry::Calendar cal;
465 size_t size = 13;
466 cal.ParseFields(d, size);
467 cout << cal << endl;
472 #endif