# r_servicebook.cc

Go to the documentation of this file.

    00001 ///
    00002 /// \file       r_servicebook.cc
    00003 ///             Blackberry database record parser class for
    00004 ///             Service Book records.
    00005 ///
    00006
    00007 /*
    00008     Copyright (C) 2005-2011, Net Direct Inc. (http://www.netdirect.ca/)
    00009
    00010     This program is free software; you can redistribute it and/or modify
    00011     it under the terms of the GNU General Public License as published by
    00012     the Free Software Foundation; either version 2 of the License, or
    00013     (at your option) any later version.
    00014
    00015     This program is distributed in the hope that it will be useful,
    00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
    00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00018
    00019     See the GNU General Public License in the COPYING file at the
    00020     root directory of this project for more details.
    00021 */
    00022
    00023 #include "r_servicebook.h"
    00024 #include "record-internal.h"
    00025 #include "protocol.h"
    00026 #include "protostructs.h"
    00027 #include "data.h"
    00028 #include "time.h"
    00029 #include "error.h"
    00030 #include "endian.h"
    00031 #include "iconv.h"
    00032 #include <ostream>
    00033 #include <iomanip>
    00034 #include <time.h>
    00035 #include <stdexcept>
    00036
    00037 #define __DEBUG_MODE__
    00038 #include "debug.h"
    00039
    00040 using namespace std;
    00041 using namespace Barry::Protocol;
    00042
    00043 namespace Barry {
    00044
    00045 ///////////////////////////////////////////////////////////////////////////////
    00046 // ServiceBookConfig class
    00047
    00048 // service book packed field codes
    00049 #define SBFCC_END                       0xffff
    00050
    00051 static FieldLink<ServiceBookConfig> ServiceBookConfigFieldLinks[] = {
    00052 //   { SBFC_DSID,        "DSID",       0, 0,    &ServiceBook::DSID, 0, 0 },
    00053    { SBFCC_END,         "End of List",0, 0,    0, 0, 0 }
    00054 };
    00055
    00056 ServiceBookConfig::ServiceBookConfig()
    00057         : Format(0)
    00058 {
    00059         Clear();
    00060 }
    00061
    00062 ServiceBookConfig::~ServiceBookConfig()
    00063 {
    00064 }
    00065
    00066 const unsigned char* ServiceBookConfig::ParseField(const unsigned char *begin,
    00067                                                    const unsigned char *end,
    00068                                                    const IConverter *ic)
    00069 {
    00070         const void *raw;
    00071         uint16_t size, type;
    00072
    00073         switch( Format )
    00074         {
    00075         case 0x01:
    00076         case 0x02:
    00077                 {
    00078                         const PackedField_02 *field = (const PackedField_02 *) begin;
    00079                         raw = field->raw;
    00080                         size = field->size;
    00081                         type = field->type;
    00082                         begin += PACKED_FIELD_02_HEADER_SIZE + size;
    00083                 }
    00084                 break;
    00085
    00086         case 0x10:
    00087                 {
    00088                         const PackedField_10 *field = (const PackedField_10 *) begin;
    00089                         raw = field->raw;
    00090                         size = field->size;
    00091                         type = field->type;
    00092                         begin += PACKED_FIELD_10_HEADER_SIZE + size;
    00093                 }
    00094                 break;
    00095
    00096         default:
    00097                 eout("------> Unknown packed field format: 0x" << std::hex <<
    00098                         (unsigned int) Format);
    00099                 throw BadPackedFormat(Format);
    00100                 return begin + 1;
    00101         }
    00102
    00103
    00104         // check size
    00105         if( begin > end )               // if begin==end, we are ok
    00106                 return begin;
    00107
    00108         if( !size )             // if field has no size, something's up
    00109                 return begin;
    00110
    00111         // cycle through the type table
    00112         for(    FieldLink<ServiceBookConfig> *b = ServiceBookConfigFieldLinks;
    00113                 b->type != SBFCC_END;
    00114                 b++ )
    00115         {
    00116                 if( b->type == type ) {
    00117                         if( b->strMember ) {
    00118                                 std::string &s = this->*(b->strMember);
    00119                                 s = ParseFieldString(raw, size-1);
    00120                                 return begin;   // done!
    00121                         }
    00122                 }
    00123         }
    00124
    00125 /*
    00126         // handle special cases
    00127         switch( type )
    00128         {
    00129         }
    00130 */
    00131
    00132         // if still not handled, add to the Unknowns list
    00133         UnknownField uf;
    00134         uf.type = type;
    00135         uf.data.assign((const char*)raw, size);
    00136         Unknowns.push_back(uf);
    00137
    00138         // return new pointer for next field
    00139         return begin;
    00140 }
    00141
    00142 void ServiceBookConfig::ParseHeader(const Data &data, size_t &offset)
    00143 {
    00144         MAKE_RECORD(const Barry::Protocol::ServiceBookConfigField, sbc, data, offset);
    00145         offset += SERVICE_BOOK_CONFIG_FIELD_HEADER_SIZE;
    00146         if( data.GetSize() >= offset ) {        // size check!
    00147                 Format = sbc->format;
    00148         }
    00149 }
    00150
    00151 void ServiceBookConfig::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
    00152 {
    00153         const unsigned char *finish = ParseCommonFields(*this,
    00154                 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
    00155         offset += finish - (data.GetData() + offset);
    00156 }
    00157
    00158 void ServiceBookConfig::BuildHeader(Data &data, size_t &offset) const
    00159 {
    00160         // make sure there is enough space
    00161         data.GetBuffer(offset + SERVICE_BOOK_CONFIG_FIELD_HEADER_SIZE);
    00162
    00163         MAKE_RECORD(Barry::Protocol::ServiceBookConfigField, sbc, data, offset);
    00164         sbc->format = Format;
    00165
    00166         offset += SERVICE_BOOK_CONFIG_FIELD_HEADER_SIZE;
    00167 }
    00168
    00169 //
    00170 // BuildFields
    00171 //
    00172 /// Build fields part of record
    00173 ///
    00174 void ServiceBookConfig::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
    00175 {
    00176         throw std::logic_error("ServiceBookConfig::Build not yet implemented");
    00177 }
    00178
    00179 void ServiceBookConfig::Clear()
    00180 {
    00181         Format = 0;
    00182         Unknowns.clear();
    00183 }
    00184
    00185 void ServiceBookConfig::Dump(std::ostream &os) const
    00186 {
    00187         os << "   ServiceBookConfig Format: " << setbase(16) << (uint16_t)Format << "\n";
    00188
    00189         // cycle through the type table
    00190         for(    const FieldLink<ServiceBookConfig> *b = ServiceBookConfigFieldLinks;
    00191                 b->type != SBFCC_END;
    00192                 b++ )
    00193         {
    00194                 if( b->strMember ) {
    00195                         const std::string &s = this->*(b->strMember);
    00196                         if( s.size() )
    00197                                 os << "      " << b->name << ": " << s << "\n";
    00198                 }
    00199                 else if( b->timeMember ) {
    00200                         time_t t = this->*(b->timeMember);
    00201                         if( t > 0 )
    00202                                 os << "      " << b->name << ": " << ctime(&t);
    00203                 }
    00204         }
    00205
    00206         // print any unknowns
    00207         os << Unknowns;
    00208         os << "   ------------------- End of Config Field\n";
    00209 }
    00210
    00211
    00212 ///////////////////////////////////////////////////////////////////////////////
    00213 // ServiceBook class
    00214
    00215 // service book field codes
    00216 #define SBFC_OLD_NAME                   0x01
    00217 #define SBFC_HIDDEN_NAME                0x02
    00218 #define SBFC_NAME                       0x03
    00219 #define SBFC_OLD_UNIQUE_ID              0x06
    00220 #define SBFC_UNIQUE_ID                  0x07
    00221 #define SBFC_CONTENT_ID                 0x08
    00222 #define SBFC_CONFIG                     0x09
    00223 #define SBFC_OLD_DESC                   0x32
    00224 #define SBFC_DESCRIPTION                0x0f
    00225 #define SBFC_DSID                       0xa1
    00226 #define SBFC_BES_DOMAIN                 0xa2
    00227 #define SBFC_USER_ID                    0xa3
    00228 #define SBFC_END                        0xffff
    00229
    00230 // private data class, containing internal structures
    00231 class ServiceBookData
    00232 {
    00233 public:
    00234         FieldLink<ServiceBook> *m_typeSet;
    00235         ServiceBookData(FieldLink<ServiceBook> *typeSet) : m_typeSet(typeSet) {}
    00236 };
    00237
    00238 // The Old/New tables contain the same fields, but use different
    00239 // type codes.  Keeping them separate yet linked makes it possible
    00240 // to convert between old and new type codes, while hopefully keeping
    00241 // things generic.
    00242 static FieldLink<ServiceBook> ServiceBookOldFieldLinks[] = {
    00243    { SBFC_OLD_NAME,      "Old Name", 0, 0,     &ServiceBook::Name, 0, 0, 0, 0, true },
    00244    { SBFC_OLD_DESC,      "Old Desc", 0, 0,     &ServiceBook::Description, 0, 0, 0, 0, true },
    00245    { SBFC_OLD_UNIQUE_ID, "Old UniqueId", 0, 0, &ServiceBook::UniqueId, 0, 0, 0, 0, false },
    00246    { SBFC_END,           "End of List", 0, 0,  0, 0, 0, 0, 0, false }
    00247 };
    00248
    00249 static FieldLink<ServiceBook> ServiceBookNewFieldLinks[] = {
    00250    { SBFC_NAME,        "Name", 0, 0,         &ServiceBook::Name, 0, 0, 0, 0, true },
    00251    { SBFC_DESCRIPTION, "Description", 0, 0,  &ServiceBook::Description, 0, 0, 0, 0, true },
    00252    { SBFC_UNIQUE_ID,   "UniqueId", 0, 0,     &ServiceBook::UniqueId, 0, 0, 0, 0, false },
    00253    { SBFC_END,         "End of List", 0, 0,  0, 0, 0, 0, 0, false }
    00254 };
    00255
    00256 // This table holds all
    00257 static FieldLink<ServiceBook> ServiceBookFieldLinks[] = {
    00258    { SBFC_HIDDEN_NAME, "Hidden Name",0, 0, &ServiceBook::HiddenName, 0, 0, 0, 0, true },
    00259    { SBFC_DSID,        "DSID",       0, 0, &ServiceBook::DSID, 0, 0, 0, 0, false },
    00260    { SBFC_CONTENT_ID,  "ContentId",  0, 0, &ServiceBook::ContentId, 0, 0, 0, 0, false },
    00261    { SBFC_BES_DOMAIN,  "BES Domain", 0, 0, &ServiceBook::BesDomain, 0, 0, 0, 0, false },
    00262    { SBFC_END,         "End of List",0, 0, 0, 0, 0, 0, 0, false }
    00263 };
    00264
    00265 // Array of conflicting tables only
    00266 static FieldLink<ServiceBook> *ServiceBookLinkTable[] = {
    00267    ServiceBookOldFieldLinks,
    00268    ServiceBookNewFieldLinks,
    00269    0
    00270 };
    00271
    00272 #define FIELDLINK_END 0xffff
    00273
    00274 template <class RecordT>
    00275 FieldLink<RecordT>* ParseFieldByTable(RecordT *rec,
    00276                                       const CommonField *field,
    00277                                       const IConverter *ic,
    00278                                       FieldLink<RecordT> *links)
    00279 {
    00280         // cycle through the type table
    00281         for( FieldLink<RecordT> *b = links; b->type != FIELDLINK_END; b++ ) {
    00282                 if( b->type == field->type ) {
    00283                         if( b->strMember ) {
    00284                                 std::string &s = rec->*(b->strMember);
    00285                                 if( s.size() ) {
    00286                                         dout(RecordT::GetDBName() << ": field '" << b->name << "' already has data (" << s << "). Overwriting.");
    00287                                 }
    00288                                 s = ParseFieldString(field);
    00289                                 if( b->iconvNeeded && ic )
    00290                                         s = ic->FromBB(s);
    00291                                 return links;
    00292                         }
    00293                         else if( b->timeMember && btohs(field->size) == 4 ) {
    00294                                 time_t &t = rec->*(b->timeMember);
    00295                                 t = min2time(field->u.min1900);
    00296                                 return links;
    00297                         }
    00298                 }
    00299         }
    00300         return 0;
    00301 }
    00302
    00303 template <class RecordT>
    00304 FieldLink<RecordT>* ParseFieldByTable(RecordT *rec,
    00305                                       const CommonField *field,
    00306                                       const IConverter *ic,
    00307                                       FieldLink<RecordT> **b)
    00308 {
    00309         for( ; *b; b++ ) {
    00310                 FieldLink<RecordT> *link =
    00311                         ParseFieldByTable<RecordT>(rec, field, ic, *b);
    00312                 if( link )
    00313                         return link;
    00314         }
    00315         return 0;
    00316 }
    00317
    00318 ServiceBook::ServiceBook()
    00319         : m_data( new ServiceBookData(ServiceBookOldFieldLinks) )
    00320         , RecordId(0)
    00321 {
    00322         Clear();
    00323 }
    00324
    00325 ServiceBook::~ServiceBook()
    00326 {
    00327 }
    00328
    00329 const unsigned char* ServiceBook::ParseField(const unsigned char *begin,
    00330                                              const unsigned char *end,
    00331                                              const IConverter *ic)
    00332 {
    00333         const CommonField *field = (const CommonField *) begin;
    00334
    00335         // advance and check size
    00336         begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
    00337         if( begin > end )               // if begin==end, we are ok
    00338                 return begin;
    00339
    00340         if( !btohs(field->size) )       // if field has no size, something's up
    00341                 return begin;
    00342
    00343         // cycle through the type tables
    00344         FieldLink<ServiceBook> *typeSet =
    00345                 ParseFieldByTable(this, field, ic, ServiceBookLinkTable);
    00346         if( typeSet ) {
    00347                 if( m_data->m_typeSet && m_data->m_typeSet != typeSet ) {
    00348                         dout("ServiceBook record has a mix of old and new field types.");
    00349                 }
    00350                 m_data->m_typeSet = typeSet;
    00351                 return begin;
    00352         }
    00353         else {
    00354                 if( ParseFieldByTable(this, field, ic, ServiceBookFieldLinks) )
    00355                         return begin;   // done!
    00356         }
    00357
    00358         // handle special cases
    00359         switch( field->type )
    00360         {
    00361         case SBFC_CONFIG:
    00362                 try {
    00363                         Data config((const void *)field->u.raw, btohs(field->size));
    00364                         size_t offset = 0;
    00365                         Config.ParseHeader(config, offset);
    00366                         Config.ParseFields(config, offset);
    00367                         return begin;   // success
    00368                 }
    00369                 catch( BadPackedFormat & ) {
    00370                         // break here so unprocessed raw packet is still
    00371                         // visible in dump
    00372                         break;
    00373                 }
    00374         }
    00375
    00376         // if still not handled, add to the Unknowns list
    00377         UnknownField uf;
    00378         uf.type = field->type;
    00379         uf.data.assign((const char*)field->u.raw, btohs(field->size));
    00380         Unknowns.push_back(uf);
    00381
    00382         // return new pointer for next field
    00383         return begin;
    00384 }
    00385
    00386 void ServiceBook::ParseHeader(const Data &data, size_t &offset)
    00387 {
    00388         // no header in this record (?)
    00389 }
    00390
    00391 void ServiceBook::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
    00392 {
    00393         const unsigned char *finish = ParseCommonFields(*this,
    00394                 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
    00395         offset += finish - (data.GetData() + offset);
    00396 }
    00397
    00398 void ServiceBook::BuildHeader(Data &data, size_t &offset) const
    00399 {
    00400         // no header in this record (?)
    00401 }
    00402
    00403 //
    00404 // BuildFields
    00405 //
    00406 /// Build fields part of record
    00407 ///
    00408 void ServiceBook::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
    00409 {
    00410         throw std::logic_error("ServiceBook::BuildFields not yet implemented");
    00411 }
    00412
    00413 void ServiceBook::Clear()
    00414 {
    00415         m_data->m_typeSet = ServiceBookOldFieldLinks;
    00416         Unknowns.clear();
    00417         Config.Clear();
    00418 }
    00419
    00420 std::string ServiceBook::GetDescription() const
    00421 {
    00422         return Name;
    00423 }
    00424
    00425 inline void FormatStr(std::ostream &os, const char *name, const std::string &str)
    00426 {
    00427         if( str.size() ) {
    00428                 os << "   " << setw(20) << name;
    00429                 os << ": " << str << "\n";
    00430         }
    00431 }
    00432
    00433 void ServiceBook::Dump(std::ostream &os) const
    00434 {
    00435         ios::fmtflags oldflags = os.setf(ios::left);
    00436         char fill = os.fill(' ');
    00437
    00438         os << "ServiceBook entry: 0x" << setbase(16) << RecordId
    00439                 << " (" << (unsigned int)RecType << ")\n";
    00440
    00441         FormatStr(os, "Name", Name);
    00442         FormatStr(os, "Hidden Name", HiddenName);
    00443         FormatStr(os, "Description", Description);
    00444         FormatStr(os, "DSID", DSID);
    00445         FormatStr(os, "Unique ID", UniqueId);
    00446         FormatStr(os, "Content ID", ContentId);
    00447         FormatStr(os, "(BES) Domain", BesDomain);
    00448
    00449         os << Config;
    00450
    00451         // print any unknowns
    00452         os << Unknowns;
    00453
    00454         // cleanup the stream
    00455         os.flags(oldflags);
    00456         os.fill(fill);
    00457 }
    00458
    00459 bool ServiceBook::operator<(const ServiceBook &other) const
    00460 {
    00461         int cmp = BesDomain.compare(other.BesDomain);
    00462         if( cmp == 0 )
    00463                 cmp = DSID.compare(other.DSID);
    00464         if( cmp == 0 )
    00465                 cmp = Name.compare(other.Name);
    00466         if( cmp == 0 )
    00467                 cmp = UniqueId.compare(other.UniqueId);
    00468         return cmp < 0;
    00469 }
    00470
    00471 } // namespace Barry
    00472