record.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       record.cc
00003 ///             Blackberry database record classes.  Help translate data
00004 ///             from data packets to useful structurs, and back.
00005 ///             This header provides the common types and classes
00006 ///             used by the general record parser classes in the
00007 ///             r_*.h files.  Only application-safe API stuff goes in
00008 ///             here.  Internal library types go in record-internal.h
00009 ///
00010 
00011 /*
00012     Copyright (C) 2005-2008, Net Direct Inc. (http://www.netdirect.ca/)
00013 
00014     This program is free software; you can redistribute it and/or modify
00015     it under the terms of the GNU General Public License as published by
00016     the Free Software Foundation; either version 2 of the License, or
00017     (at your option) any later version.
00018 
00019     This program is distributed in the hope that it will be useful,
00020     but WITHOUT ANY WARRANTY; without even the implied warranty of
00021     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 
00023     See the GNU General Public License in the COPYING file at the
00024     root directory of this project for more details.
00025 */
00026 
00027 #include "record.h"
00028 #include "record-internal.h"
00029 #include "protocol.h"
00030 #include "protostructs.h"
00031 #include "data.h"
00032 #include "time.h"
00033 #include "error.h"
00034 #include "endian.h"
00035 #include <sstream>
00036 #include <iomanip>
00037 #include <time.h>
00038 #include <string.h>
00039 #include <stdexcept>
00040 
00041 #define __DEBUG_MODE__
00042 #include "debug.h"
00043 
00044 using namespace std;
00045 using namespace Barry::Protocol;
00046 
00047 namespace Barry {
00048 
00049 //////////////////////////////////////////////////////////////////////////////
00050 // Field builder helper functions
00051 
00052 void BuildField1900(Data &data, size_t &size, uint8_t type, time_t t)
00053 {
00054         size_t timesize = COMMON_FIELD_MIN1900_SIZE;
00055         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + timesize;
00056         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
00057         CommonField *field = (CommonField *) pd;
00058 
00059         field->size = htobs(timesize);
00060         field->type = type;
00061         field->u.min1900 = time2min(t);
00062 
00063         size += fieldsize;
00064 }
00065 
00066 void BuildField(Data &data, size_t &size, uint8_t type, char c)
00067 {
00068         size_t strsize = 1;
00069         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
00070         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
00071         CommonField *field = (CommonField *) pd;
00072 
00073         field->size = htobs(strsize);
00074         field->type = type;
00075         memcpy(field->u.raw, &c, strsize);
00076 
00077         size += fieldsize;
00078 }
00079 
00080 void BuildField(Data &data, size_t &size, uint8_t type, uint16_t value)
00081 {
00082         size_t strsize = 2;
00083         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
00084         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
00085         CommonField *field = (CommonField *) pd;
00086 
00087         field->size = htobs(strsize);
00088         field->type = type;
00089 
00090         uint16_t store = htobs(value);
00091         memcpy(field->u.raw, &store, strsize);
00092 
00093         size += fieldsize;
00094 }
00095 
00096 void BuildField(Data &data, size_t &size, uint8_t type, const std::string &str)
00097 {
00098         // include null terminator
00099         BuildField(data, size, type, str.c_str(), str.size() + 1);
00100 }
00101 
00102 void BuildField(Data &data, size_t &size, uint8_t type,
00103                 const void *buf, size_t bufsize)
00104 {
00105         // include null terminator
00106         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + bufsize;
00107         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
00108         CommonField *field = (CommonField *) pd;
00109 
00110         field->size = htobs(bufsize);
00111         field->type = type;
00112         memcpy(field->u.raw, buf, bufsize);
00113 
00114         size += fieldsize;
00115 }
00116 
00117 void BuildField(Data &data, size_t &size, const Barry::UnknownField &field)
00118 {
00119         BuildField(data, size, field.type,
00120                 field.data.raw_data.data(), field.data.raw_data.size());
00121 }
00122 
00123 void BuildField(Data &data, size_t &size, uint8_t type, const Barry::Protocol::GroupLink &link)
00124 {
00125         size_t linksize = sizeof(Barry::Protocol::GroupLink);
00126         size_t fieldsize = COMMON_FIELD_HEADER_SIZE + linksize;
00127         unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
00128         CommonField *field = (CommonField *) pd;
00129 
00130         field->size = htobs(linksize);
00131         field->type = type;
00132         field->u.link = link;
00133 
00134         size += fieldsize;
00135 }
00136 
00137 std::string ParseFieldString(const Barry::Protocol::CommonField *field)
00138 {
00139         // make no assumptions here, and pass the full size in as
00140         // the maxlen, even though 99% of the time, it will be a null...
00141         // this function can be used by non-null terminated strings as well
00142         return ParseFieldString(field->u.raw, btohs(field->size));
00143 }
00144 
00145 std::string ParseFieldString(const void *data, uint16_t maxlen)
00146 {
00147         const char *str = (const char *)data;
00148 
00149         // find last non-null character, since some fields
00150         // can have multiple null terminators
00151         while( maxlen && str[maxlen-1] == 0 )
00152                 maxlen--;
00153 
00154         return std::string(str, maxlen);
00155 }
00156 
00157 
00158 ///////////////////////////////////////////////////////////////////////////////
00159 // CommandTable class
00160 
00161 CommandTable::CommandTable()
00162 {
00163 }
00164 
00165 CommandTable::~CommandTable()
00166 {
00167 }
00168 
00169 const unsigned char* CommandTable::ParseField(const unsigned char *begin,
00170                                               const unsigned char *end)
00171 {
00172         // check if there is enough data for a header
00173         const unsigned char *headend = begin + sizeof(CommandTableField);
00174         if( headend > end )
00175                 return headend;
00176 
00177         const CommandTableField *field = (const CommandTableField *) begin;
00178 
00179         // advance and check size
00180         begin += COMMAND_FIELD_HEADER_SIZE + field->size;       // size is byte
00181         if( begin > end )               // if begin==end, we are ok
00182                 return begin;
00183 
00184         if( !field->size )              // if field has no size, something's up
00185                 return begin;
00186 
00187         Command command;
00188         command.Code = field->code;
00189         command.Name.assign((const char *)field->name, field->size);
00190         Commands.push_back(command);
00191         return begin;
00192 }
00193 
00194 void CommandTable::Parse(const Data &data, size_t offset)
00195 {
00196         if( offset >= data.GetSize() )
00197                 return;
00198 
00199         const unsigned char *begin = data.GetData() + offset;
00200         const unsigned char *end = data.GetData() + data.GetSize();
00201 
00202         while( begin < end )
00203                 begin = ParseField(begin, end);
00204 }
00205 
00206 void CommandTable::Clear()
00207 {
00208         Commands.clear();
00209 }
00210 
00211 unsigned int CommandTable::GetCommand(const std::string &name) const
00212 {
00213         CommandArrayType::const_iterator b = Commands.begin();
00214         for( ; b != Commands.end(); b++ )
00215                 if( b->Name == name )
00216                         return b->Code;
00217         return 0;
00218 }
00219 
00220 void CommandTable::Dump(std::ostream &os) const
00221 {
00222         CommandArrayType::const_iterator b = Commands.begin();
00223         os << "Command table:\n";
00224         for( ; b != Commands.end(); b++ ) {
00225                 os << "    Command: 0x" << setbase(16) << b->Code
00226                    << " '" << b->Name << "'\n";
00227         }
00228 }
00229 
00230 
00231 
00232 ///////////////////////////////////////////////////////////////////////////////
00233 // RecordStateTable class
00234 
00235 RecordStateTable::RecordStateTable()
00236         : m_LastNewRecordId(1)
00237 {
00238 }
00239 
00240 RecordStateTable::~RecordStateTable()
00241 {
00242 }
00243 
00244 const unsigned char* RecordStateTable::ParseField(const unsigned char *begin,
00245                                                   const unsigned char *end)
00246 {
00247         const RecordStateTableField *field = (const RecordStateTableField *) begin;
00248 
00249         // advance and check size
00250         begin += sizeof(RecordStateTableField);
00251         if( begin > end )               // if begin==end, we are ok
00252                 return begin;
00253 
00254         State state;
00255         state.Index = btohs(field->index);
00256         state.RecordId = btohl(field->uniqueId);
00257         state.Dirty = (field->flags & BARRY_RSTF_DIRTY) != 0;
00258         state.RecType = field->rectype;
00259         state.Unknown2.assign((const char*)field->unknown2, sizeof(field->unknown2));
00260         StateMap[state.Index] = state;
00261 
00262         return begin;
00263 }
00264 
00265 void RecordStateTable::Parse(const Data &data)
00266 {
00267         size_t offset = 12;     // skipping the unknown 2 bytes at start
00268 
00269         if( offset >= data.GetSize() )
00270                 return;
00271 
00272         const unsigned char *begin = data.GetData() + offset;
00273         const unsigned char *end = data.GetData() + data.GetSize();
00274 
00275         while( begin < end )
00276                 begin = ParseField(begin, end);
00277 }
00278 
00279 void RecordStateTable::Clear()
00280 {
00281         StateMap.clear();
00282         m_LastNewRecordId = 1;
00283 }
00284 
00285 // Searches the StateMap table for RecordId, and returns the "index"
00286 // in the map if found.  Returns true if found, false if not.
00287 // pFoundIndex can be null if only the existence of the index is desired
00288 bool RecordStateTable::GetIndex(uint32_t RecordId, IndexType *pFoundIndex) const
00289 {
00290         StateMapType::const_iterator i = StateMap.begin();
00291         for( ; i != StateMap.end(); ++i ) {
00292                 if( i->second.RecordId == RecordId ) {
00293                         if( pFoundIndex )
00294                                 *pFoundIndex = i->first;
00295                         return true;
00296                 }
00297         }
00298         return false;
00299 }
00300 
00301 // Generate a new RecordId that is not in the state table.
00302 // Starts at 1 and keeps incrementing until a free one is found.
00303 uint32_t RecordStateTable::MakeNewRecordId() const
00304 {
00305         // start with next Id
00306         m_LastNewRecordId++;
00307 
00308         // make sure it doesn't already exist
00309         StateMapType::const_iterator i = StateMap.begin();
00310         while( i != StateMap.end() ) {
00311                 if( m_LastNewRecordId == i->second.RecordId ) {
00312                         m_LastNewRecordId++;            // try again
00313                         i = StateMap.begin();           // start over
00314                 }
00315                 else {
00316                         ++i;                            // next State
00317                 }
00318         }
00319         return m_LastNewRecordId;
00320 }
00321 
00322 void RecordStateTable::Dump(std::ostream &os) const
00323 {
00324         ios::fmtflags oldflags = os.setf(ios::right);
00325         char fill = os.fill(' ');
00326         bool bPrintAscii = Data::PrintAscii();
00327         Data::PrintAscii(false);
00328 
00329         os << "  Index  RecordId    Dirty  RecType" << endl;
00330         os << "-------  ----------  -----  -------" << endl;
00331 
00332         StateMapType::const_iterator b, e = StateMap.end();
00333         for( b = StateMap.begin(); b != e ; ++b ) {
00334                 const State &state = b->second;
00335 
00336                 os.fill(' ');
00337                 os << setbase(10) << setw(7) << state.Index;
00338                 os << "  0x" << setbase(16) << setfill('0') << setw(8) << state.RecordId;
00339                 os << "  " << setfill(' ') << setw(5) << (state.Dirty ? "yes" : "no");
00340                 os << "     0x" << setbase(16) << setfill('0') << setw(2) << state.RecType;
00341                 os << "   " << Data(state.Unknown2.data(), state.Unknown2.size());
00342         }
00343 
00344         // cleanup the stream
00345         os.flags(oldflags);
00346         os.fill(fill);
00347         Data::PrintAscii(bPrintAscii);
00348 }
00349 
00350 
00351 
00352 ///////////////////////////////////////////////////////////////////////////////
00353 // DatabaseDatabase class
00354 
00355 DatabaseDatabase::DatabaseDatabase()
00356 {
00357 }
00358 
00359 DatabaseDatabase::~DatabaseDatabase()
00360 {
00361 }
00362 
00363 template <class RecordType, class FieldType>
00364 void DatabaseDatabase::ParseRec(const RecordType &rec, const unsigned char *end)
00365 {
00366 }
00367 
00368 template <class FieldType>
00369 const unsigned char* DatabaseDatabase::ParseField(const unsigned char *begin,
00370                                                   const unsigned char *end)
00371 {
00372         // check if there is enough data for a header
00373         const unsigned char *headend = begin + sizeof(FieldType);
00374         if( headend > end )
00375                 return headend;
00376 
00377         // get our header
00378         const FieldType *field = (const FieldType *) begin;
00379 
00380         // advance and check size
00381         begin += sizeof(FieldType) - sizeof(field->name) + ConvertHtoB(field->nameSize);
00382         if( begin > end )               // if begin==end, we are ok
00383                 return begin;
00384 
00385         if( !ConvertHtoB(field->nameSize) ) // if field has no size, something's up
00386                 return begin;
00387 
00388         Database db;
00389         db.Number = ConvertHtoB(field->dbNumber);
00390         db.RecordCount = ConvertHtoB(field->dbRecordCount);
00391         db.Name.assign((const char *)field->name, ConvertHtoB(field->nameSize) - 1);
00392         Databases.push_back(db);
00393         return begin;
00394 }
00395 
00396 void DatabaseDatabase::Parse(const Data &data)
00397 {
00398         // check size to make sure we have up to the DBAccess operation byte
00399         if( data.GetSize() < (SB_PACKET_DBACCESS_HEADER_SIZE + 1) )
00400                 return;
00401 
00402         MAKE_PACKET(pack, data);
00403         const unsigned char *begin = 0;
00404         const unsigned char *end = data.GetData() + data.GetSize();
00405 
00406         switch( pack->u.db.u.response.operation )
00407         {
00408         case SB_DBOP_GET_DBDB:
00409                 // using the new protocol
00410                 if( data.GetSize() > SB_PACKET_DBDB_HEADER_SIZE ) {
00411                         begin = (const unsigned char *)
00412                                 &pack->u.db.u.response.u.dbdb.field[0];
00413 
00414                         // this while check is ok, since ParseField checks
00415                         // for header size
00416                         while( begin < end )
00417                                 begin = ParseField<DBDBField>(begin, end);
00418                 }
00419                 else
00420                         dout("DatabaseDatabase: not enough data for parsing");
00421                 break;
00422 
00423         case SB_DBOP_OLD_GET_DBDB:
00424                 // using the old protocol
00425                 if( data.GetSize() > SB_PACKET_OLD_DBDB_HEADER_SIZE ) {
00426                         begin = (const unsigned char *)
00427                                 &pack->u.db.u.response.u.old_dbdb.field[0];
00428 
00429                         // this while check is ok, since ParseField checks
00430                         // for header size
00431                         while( begin < end )
00432                                 begin = ParseField<OldDBDBField>(begin, end);
00433                 }
00434                 else
00435                         dout("DatabaseDatabase: not enough data for parsing");
00436                 break;
00437 
00438         default:
00439                 // unknown protocol
00440                 dout("Unknown protocol");
00441                 break;
00442         }
00443 
00444 
00445 }
00446 
00447 void DatabaseDatabase::Clear()
00448 {
00449         Databases.clear();
00450 }
00451 
00452 bool DatabaseDatabase::GetDBNumber(const std::string &name,
00453                                    unsigned int &number) const
00454 {
00455         DatabaseArrayType::const_iterator b = Databases.begin();
00456         for( ; b != Databases.end(); b++ )
00457                 if( b->Name == name ) {
00458                         number = b->Number;
00459                         return true;
00460                 }
00461         return false;
00462 }
00463 
00464 bool DatabaseDatabase::GetDBName(unsigned int number,
00465                                  std::string &name) const
00466 {
00467         DatabaseArrayType::const_iterator b = Databases.begin();
00468         for( ; b != Databases.end(); b++ )
00469                 if( b->Number == number ) {
00470                         name = b->Name;
00471                         return true;
00472                 }
00473         return false;
00474 }
00475 
00476 void DatabaseDatabase::Dump(std::ostream &os) const
00477 {
00478         DatabaseArrayType::const_iterator b = Databases.begin();
00479         os << "Database database:\n";
00480         for( ; b != Databases.end(); b++ ) {
00481                 os << "    Database: 0x" << setbase(16) << b->Number
00482                    << " '" << b->Name << "' (records: "
00483                    << setbase(10) << b->RecordCount << ")\n";
00484         }
00485 }
00486 
00487 
00488 std::ostream& operator<< (std::ostream &os, const std::vector<UnknownField> &unknowns)
00489 {
00490         std::vector<UnknownField>::const_iterator
00491                 ub = unknowns.begin(), ue = unknowns.end();
00492         if( ub != ue )
00493                 os << "    Unknowns:\n";
00494         for( ; ub != ue; ub++ ) {
00495                 os << "        Type: 0x" << setbase(16)
00496                    << (unsigned int) ub->type
00497                    << " Data:\n" << Data(ub->data.data(), ub->data.size());
00498         }
00499         return os;
00500 }
00501 
00502 
00503 
00504 ///////////////////////////////////////////////////////////////////////////////
00505 // EmailAddress class
00506 
00507 std::ostream& operator<<(std::ostream &os, const EmailAddress &msga) {
00508         os << msga.Name.c_str() << " <" << msga.Email.c_str() << ">";
00509         return os;
00510 }
00511 
00512 
00513 ///////////////////////////////////////////////////////////////////////////////
00514 // PostalAddress class
00515 
00516 //
00517 // GetLabel
00518 //
00519 /// Format a mailing address into a single string, handling missing fields.
00520 ///
00521 std::string PostalAddress::GetLabel() const
00522 {
00523         std::string address = Address1;
00524         if( Address2.size() ) {
00525                 if( address.size() )
00526                         address += "\n";
00527                 address += Address2;
00528         }
00529         if( Address3.size() ) {
00530                 if( address.size() )
00531                         address += "\n";
00532                 address += Address3;
00533         }
00534         if( address.size() )
00535                 address += "\n";
00536         if( City.size() )
00537                 address += City + " ";
00538         if( Province.size() )
00539                 address += Province + " ";
00540         if( Country.size() )
00541                 address += Country;
00542         if( address.size() )
00543                 address += "\n";
00544         if( PostalCode.size() )
00545                 address += PostalCode;
00546         
00547         return address;
00548 }
00549 
00550 void PostalAddress::Clear()
00551 {
00552         Address1.clear();
00553         Address2.clear();
00554         Address3.clear();
00555         City.clear();
00556         Province.clear();
00557         PostalCode.clear();
00558         Country.clear();
00559 }
00560 
00561 std::ostream& operator<<(std::ostream &os, const PostalAddress &post) {
00562         os << post.GetLabel();
00563         return os;
00564 }
00565 
00566 
00567 
00568 ///////////////////////////////////////////////////////////////////////////////
00569 // Date class
00570 
00571 Date::Date(const struct tm *timep)
00572 {
00573         FromTm(timep);
00574 }
00575 
00576 void Date::Clear()
00577 {
00578         Month = Day = Year = 0;
00579 }
00580 
00581 void Date::ToTm(struct tm *timep) const
00582 {
00583         memset(timep, 0, sizeof(tm));
00584         timep->tm_year = Year - 1900;
00585         timep->tm_mon = Month;
00586         timep->tm_mday = Day;
00587 }
00588 
00589 std::string Date::ToYYYYMMDD() const
00590 {
00591         std::ostringstream oss;
00592         oss     << setw(4) << Year
00593                 << setw(2) << Month + 1
00594                 << setw(2) << Day;
00595         return oss.str();
00596 }
00597 
00598 //
00599 // ToBBString
00600 //
00601 /// The Blackberry stores Birthday and Anniversary date fields
00602 /// with the format: DD/MM/YYYY
00603 ///
00604 std::string Date::ToBBString() const
00605 {
00606         std::ostringstream oss;
00607         oss     << setw(2) << Day
00608                 << Month + 1
00609                 << Year;
00610         return oss.str();
00611 }
00612 
00613 bool Date::FromTm(const struct tm *timep)
00614 {
00615         Year = timep->tm_year + 1900;
00616         Month = timep->tm_mon;
00617         Day = timep->tm_mday;
00618         return true;
00619 }
00620 
00621 bool Date::FromBBString(const std::string &str)
00622 {
00623         int m, d, y;
00624         if( 3 == sscanf(str.c_str(), "%d/%d/%d", &d, &m, &y) ) {
00625                 Year = y;
00626                 Month = m - 1;
00627                 Day = d;
00628                 return true;
00629         }
00630         return false;
00631 }
00632 
00633 bool Date::FromYYYYMMDD(const std::string &str)
00634 {
00635         int m, d, y;
00636         if( 3 == sscanf(str.c_str(), "%4d%2d%2d", &y, &m, &d) ) {
00637                 Year = y;
00638                 Month = m - 1;
00639                 Day = d;
00640                 return true;
00641         }
00642         return false;
00643 }
00644 
00645 std::ostream& operator<<(std::ostream &os, const Date &date)
00646 {
00647         os      << setw(4) << date.Year << '/'
00648                 << setw(2) << date.Month << '/'
00649                 << setw(2) << date.Day;
00650         return os;
00651 }
00652 
00653 
00654 
00655 } // namespace Barry
00656 
00657 
00658 #ifdef __TEST_MODE__
00659 
00660 #include <iostream>
00661 
00662 int main(int argc, char *argv[])
00663 {
00664         if( argc < 2 ) {
00665                 cerr << "Usage: test <datafile>" << endl;
00666                 return 1;
00667         }
00668 
00669         std::vector<Data> array;
00670         if( !LoadDataArray(argv[1], array) ) {
00671                 cerr << "Unable to load file: " << argv[1] << endl;
00672                 return 1;
00673         }
00674 
00675         cout << "Loaded " << array.size() << " items" << endl;
00676 
00677         for( std::vector<Data>::iterator b = array.begin(), e = array.end();
00678                 b != e; b++ )
00679         {
00680                 Data &d = *b;
00681 //              cout << d << endl;
00682                 if( d.GetSize() > 13 && d.GetData()[6] == 0x4f ) {
00683                         Barry::Contact contact;
00684                         size_t size = 13;
00685                         contact.ParseFields(d, size);
00686                         cout << contact << endl;
00687                         contact.DumpLdif(cout, "ou=People,dc=example,dc=com");
00688                 }
00689                 else if( d.GetSize() > 13 && d.GetData()[6] == 0x44 ) {
00690                         Barry::Calendar cal;
00691                         size_t size = 13;
00692                         cal.ParseFields(d, size);
00693                         cout << cal << endl;
00694                 }
00695         }
00696 }
00697 
00698 #endif
00699 

Generated on Wed Sep 24 21:27:32 2008 for Barry by  doxygen 1.5.1