# record.cc

Go to the documentation of this file.

    00001 ///
    00002 /// \file       record.cc
    00003 ///             Misc. Blackberry database record helper classes and functions.
    00004 ///             Helps translate data from data packets to useful structures,
    00005 ///             and back.
    00006 ///
    00007
    00008 /*
    00009     Copyright (C) 2005-2011, Net Direct Inc. (http://www.netdirect.ca/)
    00010
    00011     This program is free software; you can redistribute it and/or modify
    00012     it under the terms of the GNU General Public License as published by
    00013     the Free Software Foundation; either version 2 of the License, or
    00014     (at your option) any later version.
    00015
    00016     This program is distributed in the hope that it will be useful,
    00017     but WITHOUT ANY WARRANTY; without even the implied warranty of
    00018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00019
    00020     See the GNU General Public License in the COPYING file at the
    00021     root directory of this project for more details.
    00022 */
    00023
    00024 #include "record.h"
    00025 #include "record-internal.h"
    00026 #include "protocol.h"
    00027 #include "protostructs.h"
    00028 #include "data.h"
    00029 #include "time.h"
    00030 #include "error.h"
    00031 #include "endian.h"
    00032 #include <sstream>
    00033 #include <iomanip>
    00034 #include <time.h>
    00035 #include <string.h>
    00036 #include <stdio.h>                      // for sscanf()
    00037 #include <stdexcept>
    00038
    00039 #define __DEBUG_MODE__
    00040 #include "debug.h"
    00041
    00042 using namespace std;
    00043 using namespace Barry::Protocol;
    00044
    00045 namespace Barry {
    00046
    00047 BXEXPORT std::ostream& operator<< (std::ostream &os, const Cr2LfWrapper &str)
    00048 {
    00049         for(    std::string::const_iterator i = str.m_str.begin();
    00050                 i != str.m_str.end() && *i;
    00051                 i++)
    00052         {
    00053                 if( *i == '\r' )
    00054                         os << '\n';
    00055                 else
    00056                         os << *i;
    00057         }
    00058         return os;
    00059 }
    00060
    00061 //////////////////////////////////////////////////////////////////////////////
    00062 // Field builder helper functions
    00063
    00064 void BuildField1900(Data &data, size_t &size, uint8_t type, time_t t)
    00065 {
    00066         size_t timesize = COMMON_FIELD_MIN1900_SIZE;
    00067         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + timesize;
    00068         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
    00069         CommonField *field = (CommonField *) pd;
    00070
    00071         field->size = htobs(timesize);
    00072         field->type = type;
    00073         field->u.min1900 = time2min(t);
    00074
    00075         size += fieldsize;
    00076 }
    00077
    00078 void BuildField(Data &data, size_t &size, uint8_t type, char c)
    00079 {
    00080         BuildField(data, size, type, (uint8_t)c);
    00081 }
    00082
    00083 void BuildField(Data &data, size_t &size, uint8_t type, uint8_t c)
    00084 {
    00085         size_t strsize = 1;
    00086         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
    00087         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
    00088         CommonField *field = (CommonField *) pd;
    00089
    00090         field->size = htobs(strsize);
    00091         field->type = type;
    00092         memcpy(field->u.raw, &c, strsize);
    00093
    00094         size += fieldsize;
    00095 }
    00096
    00097 void BuildField(Data &data, size_t &size, uint8_t type, uint16_t value)
    00098 {
    00099         size_t strsize = 2;
    00100         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
    00101         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
    00102         CommonField *field = (CommonField *) pd;
    00103
    00104         field->size = htobs(strsize);
    00105         field->type = type;
    00106
    00107         uint16_t store = htobs(value);
    00108         memcpy(field->u.raw, &store, strsize);
    00109
    00110         size += fieldsize;
    00111 }
    00112
    00113 void BuildField(Data &data, size_t &size, uint8_t type, uint32_t value)
    00114 {
    00115         size_t strsize = 4;
    00116         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
    00117         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
    00118         CommonField *field = (CommonField *) pd;
    00119
    00120         field->size = htobl(strsize);
    00121         field->type = type;
    00122
    00123         uint32_t store = htobl(value);
    00124         memcpy(field->u.raw, &store, strsize);
    00125
    00126         size += fieldsize;
    00127 }
    00128
    00129 void BuildField(Data &data, size_t &size, uint8_t type, uint64_t value)
    00130 {
    00131         size_t strsize = 8;
    00132         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
    00133         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
    00134         CommonField *field = (CommonField *) pd;
    00135
    00136         field->size = htobl(strsize);
    00137         field->type = type;
    00138
    00139         uint64_t store = htobll(value);
    00140         memcpy(field->u.raw, &store, strsize);
    00141
    00142         size += fieldsize;
    00143 }
    00144
    00145 void BuildField(Data &data, size_t &size, uint8_t type, const std::string &str)
    00146 {
    00147         // include null terminator
    00148         BuildField(data, size, type, str.c_str(), str.size() + 1);
    00149 }
    00150
    00151 void BuildField(Data &data, size_t &size, uint8_t type,
    00152                 const void *buf, size_t bufsize)
    00153 {
    00154         // include null terminator
    00155         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + bufsize;
    00156         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
    00157         CommonField *field = (CommonField *) pd;
    00158
    00159         field->size = htobs(bufsize);
    00160         field->type = type;
    00161         memcpy(field->u.raw, buf, bufsize);
    00162
    00163         size += fieldsize;
    00164 }
    00165
    00166 void BuildField(Data &data, size_t &size, const Barry::UnknownField &field)
    00167 {
    00168         BuildField(data, size, field.type,
    00169                 field.data.raw_data.data(), field.data.raw_data.size());
    00170 }
    00171
    00172 void BuildField(Data &data, size_t &size, uint8_t type, const Barry::Protocol::GroupLink &link)
    00173 {
    00174         size_t linksize = sizeof(Barry::Protocol::GroupLink);
    00175         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + linksize;
    00176         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
    00177         CommonField *field = (CommonField *) pd;
    00178
    00179         field->size = htobs(linksize);
    00180         field->type = type;
    00181         field->u.link = link;
    00182
    00183         size += fieldsize;
    00184 }
    00185
    00186 std::string ParseFieldString(const Barry::Protocol::CommonField *field)
    00187 {
    00188         // make no assumptions here, and pass the full size in as
    00189         // the maxlen, even though 99% of the time, it will be a null...
    00190         // this function can be used by non-null terminated strings as well
    00191         return ParseFieldString(field->u.raw, btohs(field->size));
    00192 }
    00193
    00194 std::string ParseFieldString(const void *data, uint16_t maxlen)
    00195 {
    00196         const char *str = (const char *)data;
    00197
    00198         // find last non-null character, since some fields
    00199         // can have multiple null terminators
    00200         while( maxlen && str[maxlen-1] == 0 )
    00201                 maxlen--;
    00202
    00203         return std::string(str, maxlen);
    00204 }
    00205
    00206
    00207 ///////////////////////////////////////////////////////////////////////////////
    00208 // UnknownField
    00209
    00210 std::ostream& operator<< (std::ostream &os, const std::vector<UnknownField> &unknowns)
    00211 {
    00212         std::vector<UnknownField>::const_iterator
    00213                 ub = unknowns.begin(), ue = unknowns.end();
    00214         if( ub != ue )
    00215                 os << "    Unknowns:\n";
    00216         for( ; ub != ue; ub++ ) {
    00217                 os << "        Type: 0x" << setbase(16)
    00218                    << (unsigned int) ub->type
    00219                    << " Data:\n" << Data(ub->data.data(), ub->data.size());
    00220         }
    00221         return os;
    00222 }
    00223
    00224
    00225 ///////////////////////////////////////////////////////////////////////////////
    00226 // EmailAddress class
    00227
    00228 std::ostream& operator<<(std::ostream &os, const EmailAddress &msga) {
    00229         os << msga.Name << " <" << msga.Email << ">";
    00230         return os;
    00231 }
    00232
    00233 std::ostream& operator<<(std::ostream &os, const EmailAddressList &elist) {
    00234         for( EmailAddressList::const_iterator i = elist.begin(); i != elist.end(); ++i ) {
    00235                 if( i != elist.begin() )
    00236                         os << ", ";
    00237                 os << *i;
    00238         }
    00239         return os;
    00240 }
    00241
    00242
    00243 ///////////////////////////////////////////////////////////////////////////////
    00244 // PostalAddress class
    00245
    00246 //
    00247 // GetLabel
    00248 //
    00249 /// Format a mailing address into a single string, handling missing fields.
    00250 ///
    00251 std::string PostalAddress::GetLabel() const
    00252 {
    00253         std::string address = Address1;
    00254         if( Address2.size() ) {
    00255                 if( address.size() )
    00256                         address += "\n";
    00257                 address += Address2;
    00258         }
    00259         if( Address3.size() ) {
    00260                 if( address.size() )
    00261                         address += "\n";
    00262                 address += Address3;
    00263         }
    00264         if( address.size() )
    00265                 address += "\n";
    00266         if( City.size() )
    00267                 address += City + " ";
    00268         if( Province.size() )
    00269                 address += Province + " ";
    00270         if( Country.size() )
    00271                 address += Country;
    00272         if( address.size() )
    00273                 address += "\n";
    00274         if( PostalCode.size() )
    00275                 address += PostalCode;
    00276
    00277         return address;
    00278 }
    00279
    00280 void PostalAddress::Clear()
    00281 {
    00282         Address1.clear();
    00283         Address2.clear();
    00284         Address3.clear();
    00285         City.clear();
    00286         Province.clear();
    00287         PostalCode.clear();
    00288         Country.clear();
    00289 }
    00290
    00291 std::ostream& operator<<(std::ostream &os, const PostalAddress &post) {
    00292         os << post.GetLabel();
    00293         return os;
    00294 }
    00295
    00296
    00297
    00298 ///////////////////////////////////////////////////////////////////////////////
    00299 // Date class
    00300
    00301 Date::Date(const struct tm *timep)
    00302 {
    00303         FromTm(timep);
    00304 }
    00305
    00306 void Date::Clear()
    00307 {
    00308         Month = Day = Year = 0;
    00309 }
    00310
    00311 void Date::ToTm(struct tm *timep) const
    00312 {
    00313         memset(timep, 0, sizeof(tm));
    00314         timep->tm_year = Year - 1900;
    00315         timep->tm_mon = Month;
    00316         timep->tm_mday = Day;
    00317 }
    00318
    00319 std::string Date::ToYYYYMMDD() const
    00320 {
    00321         std::ostringstream oss;
    00322         // setfill and setw not sticky.
    00323         oss     << setw(4) << setfill('0') << dec << Year
    00324                 << setw(2) << setfill('0') << dec << (Month + 1)
    00325                 << setw(2) << setfill('0') << dec << Day;
    00326         return oss.str();
    00327 }
    00328
    00329 //
    00330 // ToBBString
    00331 //
    00332 /// The Blackberry stores Birthday and Anniversary date fields
    00333 /// with the format: DD/MM/YYYY
    00334 ///
    00335 std::string Date::ToBBString() const
    00336 {
    00337         std::ostringstream oss;
    00338         // setw() ain't 'sticky'!
    00339         oss     << setw(2) << setfill('0') << dec << Day << '/'
    00340                 << setw(2) << setfill('0') << dec << (Month + 1) << '/'
    00341                 << setw(2) << setfill('0') << dec << Year;
    00342         return oss.str();
    00343 }
    00344
    00345 bool Date::FromTm(const struct tm *timep)
    00346 {
    00347         if( !timep )
    00348                 throw std::logic_error("NULL time pointer passed to Date::FromTm");
    00349
    00350         Year = timep->tm_year + 1900;
    00351         Month = timep->tm_mon;
    00352         Day = timep->tm_mday;
    00353         return true;
    00354 }
    00355
    00356 bool Date::FromBBString(const std::string &str)
    00357 {
    00358         int m, d, y;
    00359         if( 3 == sscanf(str.c_str(), "%d/%d/%d", &d, &m, &y) ) {
    00360                 Year = y;
    00361                 Month = m - 1;
    00362                 Day = d;
    00363                 return true;
    00364         }
    00365         return false;
    00366 }
    00367
    00368 bool Date::FromYYYYMMDD(const std::string &str)
    00369 {
    00370         int m, d, y;
    00371         if( 3 == sscanf(str.c_str(), "%4d%2d%2d", &y, &m, &d) ) {
    00372                 Year = y;
    00373                 Month = m - 1;
    00374                 Day = d;
    00375                 return true;
    00376         }
    00377         return false;
    00378 }
    00379
    00380 std::ostream& operator<<(std::ostream &os, const Date &date)
    00381 {
    00382         os      << setw(4) << setfill('0') << dec << date.Year << '/'
    00383                 << setw(2) << setfill('0') << dec << (date.Month + 1) << '/'
    00384                 << setw(2) << setfill('0') << dec << date.Day;
    00385         return os;
    00386 }
    00387
    00388
    00389 ///////////////////////////////////////////////////////////////////////////////
    00390 // CategoryList class
    00391
    00392 /// Parses the given comma delimited category string into
    00393 /// this CategoryList object, appending each token to the vector.
    00394 /// Will clear vector beforehand.
    00395 void CategoryList::CategoryStr2List(const std::string &str)
    00396 {
    00397         // start fresh
    00398         clear();
    00399
    00400         if( !str.size() )
    00401                 return;
    00402
    00403         // parse the comma-delimited string to a list, stripping away
    00404         // any white space around each category name
    00405         string::size_type start = 0, end = 0, delim = str.find(',', start);
    00406         while( start != string::npos ) {
    00407                 if( delim == string::npos )
    00408                         end = str.size() - 1;
    00409                 else
    00410                         end = delim - 1;
    00411
    00412                 // strip surrounding whitespace
    00413                 while( str[start] == ' ' )
    00414                         start++;
    00415                 while( end && str[end] == ' ' )
    00416                         end--;
    00417
    00418                 if( start <= end ) {
    00419                         string token = str.substr(start, end-start+1);
    00420                         push_back(token);
    00421                 }
    00422
    00423                 // next
    00424                 start = delim;
    00425                 if( start != string::npos )
    00426                         start++;
    00427                 delim = str.find(',', start);
    00428         }
    00429 }
    00430
    00431 /// Turns the current vectory into a comma delimited category
    00432 /// string suitable for use in Calendar, Task, and Memo protocol values.
    00433 void CategoryList::CategoryList2Str(std::string &str) const
    00434 {
    00435         str.clear();
    00436
    00437         Barry::CategoryList::const_iterator i = begin();
    00438         for( ; i != end(); ++i ) {
    00439                 if( str.size() )
    00440                         str += ", ";
    00441                 str += *i;
    00442         }
    00443 }
    00444
    00445
    00446 } // namespace Barry
    00447
    00448
    00449 #ifdef __TEST_MODE__
    00450
    00451 #include <iostream>
    00452
    00453 int main(int argc, char *argv[])
    00454 {
    00455         if( argc < 2 ) {
    00456                 cerr << "Usage: test <datafile>" << endl;
    00457                 return 1;
    00458         }
    00459
    00460         std::vector<Data> array;
    00461         if( !LoadDataArray(argv[1], array) ) {
    00462                 cerr << "Unable to load file: " << argv[1] << endl;
    00463                 return 1;
    00464         }
    00465
    00466         cout << "Loaded " << array.size() << " items" << endl;
    00467
    00468         for( std::vector<Data>::iterator b = array.begin(), e = array.end();
    00469                 b != e; b++ )
    00470         {
    00471                 Data &d = *b;
    00472 //              cout << d << endl;
    00473                 if( d.GetSize() > 13 && d.GetData()[6] == 0x4f ) {
    00474                         Barry::Contact contact;
    00475                         size_t size = 13;
    00476                         contact.ParseFields(d, size);
    00477                         cout << contact << endl;
    00478                         contact.DumpLdif(cout, "ou=People,dc=example,dc=com");
    00479                 }
    00480                 else if( d.GetSize() > 13 && d.GetData()[6] == 0x44 ) {
    00481                         Barry::Calendar cal;
    00482                         size_t size = 13;
    00483                         cal.ParseFields(d, size);
    00484                         cout << cal << endl;
    00485                 }
    00486         }
    00487 }
    00488
    00489 #endif
    00490