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-2012, 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 "protostructs.h"
00027 #include "data.h"
00028 #include "time.h"
00029 #include "error.h"
00030 #include "endian.h"
00031 #include "trim.h"
00032 #include "ios_state.h"
00033 #include "parser.h"
00034 #include <sstream>
00035 #include <iomanip>
00036 #include <time.h>
00037 #include <string.h>
00038 #include <stdio.h>                      // for sscanf()
00039 #include <stdexcept>
00040 
00041 // for DBNamedFieldCmp below
00042 #include "recordtmpl.h"
00043 #include "r_calendar.h"
00044 #include "r_calllog.h"
00045 #include "r_bookmark.h"
00046 #include "r_contact.h"
00047 #include "r_cstore.h"
00048 #include "r_memo.h"
00049 #include "r_message.h"
00050 #include "r_servicebook.h"
00051 #include "r_task.h"
00052 #include "r_pin_message.h"
00053 #include "r_saved_message.h"
00054 #include "r_sms.h"
00055 #include "r_folder.h"
00056 #include "r_timezone.h"
00057 #include "r_hhagent.h"
00058 
00059 #define __DEBUG_MODE__
00060 #include "debug.h"
00061 
00062 using namespace std;
00063 using namespace Barry::Protocol;
00064 
00065 namespace Barry {
00066 
00067 std::ostream& operator<< (std::ostream &os, const Cr2LfWrapper &str)
00068 {
00069         for(    std::string::const_iterator i = str.m_str.begin();
00070                 i != str.m_str.end() && *i;
00071                 i++)
00072         {
00073                 if( *i == '\r' )
00074                         os << '\n';
00075                 else
00076                         os << *i;
00077         }
00078         return os;
00079 }
00080 
00081 std::ostream& operator<< (std::ostream &os, const TimeT &t)
00082 {
00083         // strip the trailing newline
00084         string output = ctime(&t.Time);
00085         while( output.size() &&
00086                 (output[output.size()-1] == '\n' ||
00087                         output[output.size()-1] == '\r') )
00088         {
00089                 output.resize(output.size() - 1);
00090         }
00091 
00092         os << output;
00093         return os;
00094 }
00095 
00096 //////////////////////////////////////////////////////////////////////////////
00097 // Field builder helper functions
00098 
00099 void BuildField1900(Data &data, size_t &size, uint8_t type, time_t t)
00100 {
00101         size_t timesize = COMMON_FIELD_MIN1900_SIZE;
00102         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + timesize;
00103         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
00104         CommonField *field = (CommonField *) pd;
00105 
00106         field->size = htobs(timesize);
00107         field->type = type;
00108         field->u.min1900 = time2min(t);
00109 
00110         size += fieldsize;
00111 }
00112 
00113 void BuildField(Data &data, size_t &size, uint8_t type, char c)
00114 {
00115         BuildField(data, size, type, (uint8_t)c);
00116 }
00117 
00118 void BuildField(Data &data, size_t &size, uint8_t type, uint8_t c)
00119 {
00120         size_t strsize = 1;
00121         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
00122         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
00123         CommonField *field = (CommonField *) pd;
00124 
00125         field->size = htobs(strsize);
00126         field->type = type;
00127         memcpy(field->u.raw, &c, strsize);
00128 
00129         size += fieldsize;
00130 }
00131 
00132 void BuildField(Data &data, size_t &size, uint8_t type, uint16_t value)
00133 {
00134         size_t strsize = 2;
00135         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
00136         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
00137         CommonField *field = (CommonField *) pd;
00138 
00139         field->size = htobs(strsize);
00140         field->type = type;
00141 
00142         uint16_t store = htobs(value);
00143         memcpy(field->u.raw, &store, strsize);
00144 
00145         size += fieldsize;
00146 }
00147 
00148 void BuildField(Data &data, size_t &size, uint8_t type, uint32_t value)
00149 {
00150         size_t strsize = 4;
00151         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
00152         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
00153         CommonField *field = (CommonField *) pd;
00154 
00155         field->size = htobl(strsize);
00156         field->type = type;
00157 
00158         uint32_t store = htobl(value);
00159         memcpy(field->u.raw, &store, strsize);
00160 
00161         size += fieldsize;
00162 }
00163 
00164 void BuildField(Data &data, size_t &size, uint8_t type, uint64_t value)
00165 {
00166         size_t strsize = 8;
00167         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
00168         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
00169         CommonField *field = (CommonField *) pd;
00170 
00171         field->size = htobl(strsize);
00172         field->type = type;
00173 
00174         uint64_t store = htobll(value);
00175         memcpy(field->u.raw, &store, strsize);
00176 
00177         size += fieldsize;
00178 }
00179 
00180 void BuildField(Data &data, size_t &size, uint8_t type, const std::string &str)
00181 {
00182         // include null terminator
00183         BuildField(data, size, type, str.c_str(), str.size() + 1);
00184 }
00185 
00186 void BuildField(Data &data, size_t &size, uint8_t type,
00187                 const void *buf, size_t bufsize)
00188 {
00189         // include null terminator
00190         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + bufsize;
00191         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
00192         CommonField *field = (CommonField *) pd;
00193 
00194         field->size = htobs(bufsize);
00195         field->type = type;
00196         memcpy(field->u.raw, buf, bufsize);
00197 
00198         size += fieldsize;
00199 }
00200 
00201 void BuildField(Data &data, size_t &size, const Barry::UnknownField &field)
00202 {
00203         BuildField(data, size, field.type,
00204                 field.data.raw_data.data(), field.data.raw_data.size());
00205 }
00206 
00207 void BuildField(Data &data, size_t &size, uint8_t type, const Barry::Protocol::GroupLink &link)
00208 {
00209         size_t linksize = sizeof(Barry::Protocol::GroupLink);
00210         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + linksize;
00211         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
00212         CommonField *field = (CommonField *) pd;
00213 
00214         field->size = htobs(linksize);
00215         field->type = type;
00216         field->u.link = link;
00217 
00218         size += fieldsize;
00219 }
00220 
00221 std::string ParseFieldString(const Barry::Protocol::CommonField *field)
00222 {
00223         // make no assumptions here, and pass the full size in as
00224         // the maxlen, even though 99% of the time, it will be a null...
00225         // this function can be used by non-null terminated strings as well
00226         return ParseFieldString(field->u.raw, btohs(field->size));
00227 }
00228 
00229 std::string ParseFieldString(const void *data, uint16_t maxlen)
00230 {
00231         const char *str = (const char *)data;
00232 
00233         // find last non-null character, since some fields
00234         // can have multiple null terminators
00235         while( maxlen && str[maxlen-1] == 0 )
00236                 maxlen--;
00237 
00238         return std::string(str, maxlen);
00239 }
00240 
00241 
00242 ///////////////////////////////////////////////////////////////////////////////
00243 // UnknownField
00244 
00245 std::ostream& operator<< (std::ostream &os, const std::vector<UnknownField> &unknowns)
00246 {
00247         ios_format_state state(os);
00248 
00249         std::vector<UnknownField>::const_iterator
00250                 ub = unknowns.begin(), ue = unknowns.end();
00251         if( ub != ue )
00252                 os << "    Unknowns:\n";
00253         for( ; ub != ue; ub++ ) {
00254                 os << "        Type: 0x" << setbase(16)
00255                    << (unsigned int) ub->type
00256                    << " Data:\n" << Data(ub->data.data(), ub->data.size());
00257         }
00258         return os;
00259 }
00260 
00261 
00262 ///////////////////////////////////////////////////////////////////////////////
00263 // EmailList typedef
00264 
00265 std::ostream& operator<< (std::ostream &os, const EmailList &list)
00266 {
00267         for( EmailList::const_iterator b = list.begin(), e = list.end();
00268                 b != e;
00269                 ++b )
00270         {
00271                 if( b != list.begin() )
00272                         os << ", ";
00273                 os << *b;
00274         }
00275         return os;
00276 }
00277 
00278 ///////////////////////////////////////////////////////////////////////////////
00279 // EmailAddress class
00280 
00281 EmailAddress::EmailAddress(const std::string &complex_address)
00282 {
00283         size_t end = complex_address.rfind('>');
00284         size_t start = complex_address.rfind('<');
00285         if( start == string::npos || end == string::npos || start > end ) {
00286                 // simple address, add it
00287                 Email = complex_address;
00288                 Inplace::trim(Email);
00289         }
00290         else {
00291                 Name = complex_address.substr(0, start);
00292                 Inplace::trim(Name);
00293 
00294                 Email = complex_address.substr(start+1, end - start - 1);
00295                 Inplace::trim(Email);
00296         }
00297 }
00298 
00299 std::ostream& operator<<(std::ostream &os, const EmailAddress &msga)
00300 {
00301         if( msga.Name.size() )
00302                 os << msga.Name << " <";
00303         os << msga.Email;
00304         if( msga.Name.size() )
00305                 os << ">";
00306         return os;
00307 }
00308 
00309 
00310 ///////////////////////////////////////////////////////////////////////////////
00311 // EmailAddressList class
00312 
00313 std::string EmailAddressList::ToCommaSeparated() const
00314 {
00315         std::ostringstream oss;
00316         oss << *this;
00317         return oss.str();
00318 }
00319 
00320 /// Adds every email address found in the comma separated list.
00321 /// Does not clear() first.
00322 void EmailAddressList::AddCommaSeparated(const std::string &list)
00323 {
00324         istringstream iss(list);
00325         string address;
00326         iss >> ws;
00327 
00328         while( getline(iss, address, ',') ) {
00329                 // trim any trailing whitespace in the address
00330                 size_t len = address.size();
00331                 while( len && ::isspace(address[len-1]) )
00332                         address.resize(len-1);
00333 
00334                 // add to list if anything left
00335                 if( address.size() ) {
00336                         EmailAddress ea(address);
00337                         push_back(ea);
00338                 }
00339         }
00340 }
00341 
00342 std::ostream& operator<<(std::ostream &os, const EmailAddressList &elist)
00343 {
00344         for( EmailAddressList::const_iterator i = elist.begin(); i != elist.end(); ++i ) {
00345                 if( i != elist.begin() )
00346                         os << ", ";
00347                 os << *i;
00348         }
00349         return os;
00350 }
00351 
00352 
00353 ///////////////////////////////////////////////////////////////////////////////
00354 // PostalAddress class
00355 
00356 //
00357 // GetLabel
00358 //
00359 /// Format a mailing address into a single string, handling missing fields.
00360 ///
00361 std::string PostalAddress::GetLabel() const
00362 {
00363         std::string address = Address1;
00364         if( Address2.size() ) {
00365                 if( address.size() )
00366                         address += "\n";
00367                 address += Address2;
00368         }
00369         if( Address3.size() ) {
00370                 if( address.size() )
00371                         address += "\n";
00372                 address += Address3;
00373         }
00374         if( address.size() )
00375                 address += "\n";
00376         if( City.size() )
00377                 address += City + " ";
00378         if( Province.size() )
00379                 address += Province + " ";
00380         if( Country.size() )
00381                 address += Country;
00382         if( address.size() )
00383                 address += "\n";
00384         if( PostalCode.size() )
00385                 address += PostalCode;
00386 
00387         return address;
00388 }
00389 
00390 void PostalAddress::Clear()
00391 {
00392         Address1.clear();
00393         Address2.clear();
00394         Address3.clear();
00395         City.clear();
00396         Province.clear();
00397         PostalCode.clear();
00398         Country.clear();
00399 }
00400 
00401 std::ostream& operator<<(std::ostream &os, const PostalAddress &post) {
00402         os << post.GetLabel();
00403         return os;
00404 }
00405 
00406 
00407 
00408 ///////////////////////////////////////////////////////////////////////////////
00409 // Date class
00410 
00411 Date::Date(const struct tm *timep)
00412 {
00413         FromTm(timep);
00414 }
00415 
00416 void Date::Clear()
00417 {
00418         Month = Day = Year = 0;
00419 }
00420 
00421 void Date::ToTm(struct tm *timep) const
00422 {
00423         memset(timep, 0, sizeof(tm));
00424         timep->tm_year = Year - 1900;
00425         timep->tm_mon = Month;
00426         timep->tm_mday = Day;
00427 }
00428 
00429 std::string Date::ToYYYYMMDD() const
00430 {
00431         std::ostringstream oss;
00432         // setfill and setw not sticky.
00433         oss     << setw(4) << setfill('0') << dec << Year
00434                 << setw(2) << setfill('0') << dec << (Month + 1)
00435                 << setw(2) << setfill('0') << dec << Day;
00436         return oss.str();
00437 }
00438 
00439 //
00440 // ToBBString
00441 //
00442 /// The Blackberry stores Birthday and Anniversary date fields
00443 /// with the format: DD/MM/YYYY
00444 ///
00445 std::string Date::ToBBString() const
00446 {
00447         std::ostringstream oss;
00448         // setw() ain't 'sticky'!
00449         oss     << setw(2) << setfill('0') << dec << Day << '/'
00450                 << setw(2) << setfill('0') << dec << (Month + 1) << '/'
00451                 << setw(2) << setfill('0') << dec << Year;
00452         return oss.str();
00453 }
00454 
00455 bool Date::FromTm(const struct tm *timep)
00456 {
00457         if( !timep )
00458                 throw std::logic_error("NULL time pointer passed to Date::FromTm");
00459 
00460         Year = timep->tm_year + 1900;
00461         Month = timep->tm_mon;
00462         Day = timep->tm_mday;
00463         return true;
00464 }
00465 
00466 bool Date::FromBBString(const std::string &str)
00467 {
00468         int m, d, y;
00469         if( 3 == sscanf(str.c_str(), "%d/%d/%d", &d, &m, &y) ) {
00470                 Year = y;
00471                 Month = m - 1;
00472                 Day = d;
00473                 return true;
00474         }
00475         return false;
00476 }
00477 
00478 bool Date::FromYYYYMMDD(const std::string &str)
00479 {
00480         int m, d, y;
00481         if( 3 == sscanf(str.c_str(), "%4d%2d%2d", &y, &m, &d) ) {
00482                 Year = y;
00483                 Month = m - 1;
00484                 Day = d;
00485                 return true;
00486         }
00487         return false;
00488 }
00489 
00490 std::ostream& operator<<(std::ostream &os, const Date &date)
00491 {
00492         ios_format_state state(os);
00493 
00494         os.setf(ios::right);
00495         os.fill('0');
00496 
00497         os      << setw(4) << dec << date.Year << '/'
00498                 << setw(2) << dec << (date.Month + 1) << '/'
00499                 << setw(2) << dec << date.Day;
00500 
00501         return os;
00502 }
00503 
00504 
00505 ///////////////////////////////////////////////////////////////////////////////
00506 // CategoryList class
00507 
00508 /// Parses the given comma delimited category string into
00509 /// this CategoryList object, appending each token to the vector.
00510 /// Will clear vector beforehand.
00511 void CategoryList::CategoryStr2List(const std::string &str)
00512 {
00513         // start fresh
00514         clear();
00515 
00516         if( !str.size() )
00517                 return;
00518 
00519         // parse the comma-delimited string to a list, stripping away
00520         // any white space around each category name
00521         string::size_type start = 0, end = 0, delim = str.find(',', start);
00522         while( start != string::npos ) {
00523                 if( delim == string::npos )
00524                         end = str.size() - 1;
00525                 else
00526                         end = delim - 1;
00527 
00528                 // strip surrounding whitespace
00529                 while( str[start] == ' ' )
00530                         start++;
00531                 while( end && str[end] == ' ' )
00532                         end--;
00533 
00534                 if( start <= end ) {
00535                         string token = str.substr(start, end-start+1);
00536                         push_back(token);
00537                 }
00538 
00539                 // next
00540                 start = delim;
00541                 if( start != string::npos )
00542                         start++;
00543                 delim = str.find(',', start);
00544         }
00545 }
00546 
00547 /// Turns the current vectory into a comma delimited category
00548 /// string suitable for use in Calendar, Task, and Memo protocol values.
00549 void CategoryList::CategoryList2Str(std::string &str) const
00550 {
00551         str.clear();
00552 
00553         Barry::CategoryList::const_iterator i = begin();
00554         for( ; i != end(); ++i ) {
00555                 if( str.size() )
00556                         str += ", ";
00557                 str += *i;
00558         }
00559 }
00560 
00561 std::ostream& operator<<(std::ostream &os, const CategoryList &cl)
00562 {
00563         string buf;
00564         cl.CategoryList2Str(buf);
00565         os << buf;
00566         return os;
00567 }
00568 
00569 ///////////////////////////////////////////////////////////////////////////////
00570 // EnumConstants class
00571 
00572 void EnumConstants::AddConstant(const char *name,
00573                                 const std::string &display,
00574                                 int val)
00575 {
00576         m_constants.push_back(EnumConstant(name, display, val));
00577 }
00578 
00579 const EnumConstants::EnumConstant& EnumConstants::GetConstant(int value) const
00580 {
00581         for( EnumConstantList::const_iterator b = m_constants.begin(), e = m_constants.end();
00582                 b != e;
00583                 ++b )
00584         {
00585                 if( b->Value == value )
00586                         return *b;
00587         }
00588 
00589         // not found in list
00590         throw std::logic_error("Enum value not found in constant list");
00591 }
00592 
00593 const char* EnumConstants::GetName(int value) const
00594 {
00595         return GetConstant(value).Name;
00596 }
00597 
00598 const std::string& EnumConstants::GetDisplayName(int value) const
00599 {
00600         return GetConstant(value).DisplayName;
00601 }
00602 
00603 bool EnumConstants::IsConstantValid(int value) const
00604 {
00605         for( EnumConstantList::const_iterator b = m_constants.begin(), e = m_constants.end();
00606                 b != e;
00607                 ++b )
00608         {
00609                 if( b->Value == value )
00610                         return true;
00611         }
00612 
00613         // not found in list, so not a valid constant
00614         return false;
00615 }
00616 
00617 
00618 //////////////////////////////////////////////////////////////////////////////
00619 // DBNamedFieldCmp class
00620 
00621 DBNamedFieldCmp::DBNamedFieldCmp(const std::string &field_name,
00622                                         const Barry::IConverter *ic)
00623         : m_name(field_name)
00624         , m_ic(ic)
00625 {
00626 }
00627 
00628 bool DBNamedFieldCmp::operator() (const Barry::DBData &a,
00629                                 const Barry::DBData &b) const
00630 {
00631 #undef HANDLE_PARSER
00632 #define HANDLE_PARSER(tname) \
00633         else if( tname::GetDBName() == a.GetDBName() ) { \
00634                 tname rec1, rec2; \
00635                 ParseDBData(a, rec1, m_ic); \
00636                 ParseDBData(b, rec2, m_ic); \
00637                 return NamedFieldCmp<tname>(m_name).operator()(rec1, rec2); \
00638         }
00639 
00640         if( a.GetDBName() != b.GetDBName() ) {
00641                 throw logic_error("Different database types in DBNamedFieldCmp");
00642         }
00643         // fall through and use else's
00644         ALL_KNOWN_PARSER_TYPES
00645 
00646         throw logic_error("Unknown database in DBNamedFieldCmp::operator()");
00647 }
00648 
00649 } // namespace Barry
00650 
00651 
00652 #ifdef __TEST_MODE__
00653 
00654 #include <iostream>
00655 
00656 int main(int argc, char *argv[])
00657 {
00658         if( argc < 2 ) {
00659                 cerr << "Usage: test <datafile>" << endl;
00660                 return 1;
00661         }
00662 
00663         std::vector<Data> array;
00664         if( !LoadDataArray(argv[1], array) ) {
00665                 cerr << "Unable to load file: " << argv[1] << endl;
00666                 return 1;
00667         }
00668 
00669         cout << "Loaded " << array.size() << " items" << endl;
00670 
00671         for( std::vector<Data>::iterator b = array.begin(), e = array.end();
00672                 b != e; b++ )
00673         {
00674                 Data &d = *b;
00675 //              cout << d << endl;
00676                 if( d.GetSize() > 13 && d.GetData()[6] == 0x4f ) {
00677                         Barry::Contact contact;
00678                         size_t size = 13;
00679                         contact.ParseFields(d, size);
00680                         cout << contact << endl;
00681                         contact.DumpLdif(cout, "ou=People,dc=example,dc=com");
00682                 }
00683                 else if( d.GetSize() > 13 && d.GetData()[6] == 0x44 ) {
00684                         Barry::Calendar cal;
00685                         size_t size = 13;
00686                         cal.ParseFields(d, size);
00687                         cout << cal << endl;
00688                 }
00689         }
00690 }
00691 
00692 #endif
00693