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-2012, 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 #include "ios_state.h"
00037 
00038 #define __DEBUG_MODE__
00039 #include "debug.h"
00040 
00041 using namespace std;
00042 using namespace Barry::Protocol;
00043 
00044 namespace Barry {
00045 
00046 ///////////////////////////////////////////////////////////////////////////////
00047 // ServiceBookConfig class
00048 
00049 // service book packed field codes
00050 #define SBFCC_END                       0xffff
00051 
00052 static FieldLink<ServiceBookConfig> ServiceBookConfigFieldLinks[] = {
00053 //   { SBFC_DSID,        "DSID",       0, 0,    &ServiceBook::DSID, 0, 0 },
00054    { SBFCC_END,         "End of List",0, 0,    0, 0, 0 }
00055 };
00056 
00057 ServiceBookConfig::ServiceBookConfig()
00058         : Format(0)
00059 {
00060         Clear();
00061 }
00062 
00063 ServiceBookConfig::~ServiceBookConfig()
00064 {
00065 }
00066 
00067 const unsigned char* ServiceBookConfig::ParseField(const unsigned char *begin,
00068                                                    const unsigned char *end,
00069                                                    const IConverter *ic)
00070 {
00071         const void *raw;
00072         uint16_t size, type;
00073 
00074         switch( Format )
00075         {
00076         case 0x01:
00077         case 0x02:
00078                 {
00079                         const PackedField_02 *field = (const PackedField_02 *) begin;
00080                         raw = field->raw;
00081                         size = field->size;
00082                         type = field->type;
00083                         begin += PACKED_FIELD_02_HEADER_SIZE + size;
00084                 }
00085                 break;
00086 
00087         case 0x10:
00088                 {
00089                         const PackedField_10 *field = (const PackedField_10 *) begin;
00090                         raw = field->raw;
00091                         size = field->size;
00092                         type = field->type;
00093                         begin += PACKED_FIELD_10_HEADER_SIZE + size;
00094                 }
00095                 break;
00096 
00097         default:
00098                 dout("------> Unknown packed field format: 0x" << std::hex <<
00099                         (unsigned int) Format);
00100                 throw BadPackedFormat(Format);
00101                 return begin + 1;
00102         }
00103 
00104 
00105         // check size
00106         if( begin > end )               // if begin==end, we are ok
00107                 return begin;
00108 
00109         if( !size )             // if field has no size, something's up
00110                 return begin;
00111 
00112         // cycle through the type table
00113         for(    FieldLink<ServiceBookConfig> *b = ServiceBookConfigFieldLinks;
00114                 b->type != SBFCC_END;
00115                 b++ )
00116         {
00117                 if( b->type == type ) {
00118                         if( b->strMember ) {
00119                                 std::string &s = this->*(b->strMember);
00120                                 s = ParseFieldString(raw, size-1);
00121                                 return begin;   // done!
00122                         }
00123                 }
00124         }
00125 
00126 /*
00127         // handle special cases
00128         switch( type )
00129         {
00130         }
00131 */
00132 
00133         // if still not handled, add to the Unknowns list
00134         UnknownField uf;
00135         uf.type = type;
00136         uf.data.assign((const char*)raw, size);
00137         Unknowns.push_back(uf);
00138 
00139         // return new pointer for next field
00140         return begin;
00141 }
00142 
00143 void ServiceBookConfig::ParseHeader(const Data &data, size_t &offset)
00144 {
00145         MAKE_RECORD(const Barry::Protocol::ServiceBookConfigField, sbc, data, offset);
00146         offset += SERVICE_BOOK_CONFIG_FIELD_HEADER_SIZE;
00147         if( data.GetSize() >= offset ) {        // size check!
00148                 Format = sbc->format;
00149         }
00150 }
00151 
00152 void ServiceBookConfig::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
00153 {
00154         const unsigned char *finish = ParseCommonFields(*this,
00155                 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
00156         offset += finish - (data.GetData() + offset);
00157 }
00158 
00159 void ServiceBookConfig::Validate() const
00160 {
00161 }
00162 
00163 void ServiceBookConfig::BuildHeader(Data &data, size_t &offset) const
00164 {
00165         // make sure there is enough space
00166         data.GetBuffer(offset + SERVICE_BOOK_CONFIG_FIELD_HEADER_SIZE);
00167 
00168         MAKE_RECORD(Barry::Protocol::ServiceBookConfigField, sbc, data, offset);
00169         sbc->format = Format;
00170 
00171         offset += SERVICE_BOOK_CONFIG_FIELD_HEADER_SIZE;
00172 }
00173 
00174 //
00175 // BuildFields
00176 //
00177 /// Build fields part of record
00178 ///
00179 void ServiceBookConfig::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
00180 {
00181         throw std::logic_error("ServiceBookConfig::Build not yet implemented");
00182 }
00183 
00184 void ServiceBookConfig::Clear()
00185 {
00186         Format = 0;
00187         Unknowns.clear();
00188 }
00189 
00190 void ServiceBookConfig::Dump(std::ostream &os) const
00191 {
00192         ios_format_state state(os);
00193 
00194         os << "   ServiceBookConfig Format: " << setbase(16) << (uint16_t)Format << "\n";
00195 
00196         // cycle through the type table
00197         for(    const FieldLink<ServiceBookConfig> *b = ServiceBookConfigFieldLinks;
00198                 b->type != SBFCC_END;
00199                 b++ )
00200         {
00201                 if( b->strMember ) {
00202                         const std::string &s = this->*(b->strMember);
00203                         if( s.size() )
00204                                 os << "      " << b->name << ": " << s << "\n";
00205                 }
00206                 else if( b->timeMember ) {
00207                         TimeT t = this->*(b->timeMember);
00208                         if( t.Time> 0 )
00209                                 os << "      " << b->name << ": " << t << "\n";
00210                 }
00211         }
00212 
00213         // print any unknowns
00214         os << Unknowns;
00215         os << "   ------------------- End of Config Field\n";
00216 }
00217 
00218 
00219 ///////////////////////////////////////////////////////////////////////////////
00220 // ServiceBook class
00221 
00222 // service book field codes
00223 #define SBFC_OLD_NAME                   0x01
00224 #define SBFC_HIDDEN_NAME                0x02
00225 #define SBFC_NAME                       0x03
00226 #define SBFC_OLD_UNIQUE_ID              0x06
00227 #define SBFC_UNIQUE_ID                  0x07
00228 #define SBFC_CONTENT_ID                 0x08
00229 #define SBFC_CONFIG                     0x09
00230 #define SBFC_OLD_DESC                   0x32
00231 #define SBFC_DESCRIPTION                0x0f
00232 #define SBFC_DSID                       0xa1
00233 #define SBFC_BES_DOMAIN                 0xa2
00234 #define SBFC_USER_ID                    0xa3
00235 #define SBFC_END                        0xffff
00236 
00237 // private data class, containing internal structures
00238 class ServiceBookData
00239 {
00240 public:
00241         FieldLink<ServiceBook> *m_typeSet;
00242         ServiceBookData(FieldLink<ServiceBook> *typeSet) : m_typeSet(typeSet) {}
00243 };
00244 
00245 // The Old/New tables contain the same fields, but use different
00246 // type codes.  Keeping them separate yet linked makes it possible
00247 // to convert between old and new type codes, while hopefully keeping
00248 // things generic.
00249 static FieldLink<ServiceBook> ServiceBookOldFieldLinks[] = {
00250    { SBFC_OLD_NAME,      "Old Name", 0, 0,     &ServiceBook::Name, 0, 0, 0, 0, true },
00251    { SBFC_OLD_DESC,      "Old Desc", 0, 0,     &ServiceBook::Description, 0, 0, 0, 0, true },
00252    { SBFC_OLD_UNIQUE_ID, "Old 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 static FieldLink<ServiceBook> ServiceBookNewFieldLinks[] = {
00257    { SBFC_NAME,        "Name", 0, 0,         &ServiceBook::Name, 0, 0, 0, 0, true },
00258    { SBFC_DESCRIPTION, "Description", 0, 0,  &ServiceBook::Description, 0, 0, 0, 0, true },
00259    { SBFC_UNIQUE_ID,   "UniqueId", 0, 0,     &ServiceBook::UniqueId, 0, 0, 0, 0, false },
00260    { SBFC_END,         "End of List", 0, 0,  0, 0, 0, 0, 0, false }
00261 };
00262 
00263 // This table holds all
00264 static FieldLink<ServiceBook> ServiceBookFieldLinks[] = {
00265    { SBFC_HIDDEN_NAME, "Hidden Name",0, 0, &ServiceBook::HiddenName, 0, 0, 0, 0, true },
00266    { SBFC_DSID,        "DSID",       0, 0, &ServiceBook::DSID, 0, 0, 0, 0, false },
00267    { SBFC_CONTENT_ID,  "ContentId",  0, 0, &ServiceBook::ContentId, 0, 0, 0, 0, false },
00268    { SBFC_BES_DOMAIN,  "BES Domain", 0, 0, &ServiceBook::BesDomain, 0, 0, 0, 0, false },
00269    { SBFC_END,         "End of List",0, 0, 0, 0, 0, 0, 0, false }
00270 };
00271 
00272 // Array of conflicting tables only
00273 static FieldLink<ServiceBook> *ServiceBookLinkTable[] = {
00274    ServiceBookOldFieldLinks,
00275    ServiceBookNewFieldLinks,
00276    0
00277 };
00278 
00279 #define FIELDLINK_END 0xffff
00280 
00281 template <class RecordT>
00282 FieldLink<RecordT>* ParseFieldByTable(RecordT *rec,
00283                                       const CommonField *field,
00284                                       const IConverter *ic,
00285                                       FieldLink<RecordT> *links)
00286 {
00287         // cycle through the type table
00288         for( FieldLink<RecordT> *b = links; b->type != FIELDLINK_END; b++ ) {
00289                 if( b->type == field->type ) {
00290                         if( b->strMember ) {
00291                                 std::string &s = rec->*(b->strMember);
00292                                 if( s.size() ) {
00293                                         dout(RecordT::GetDBName() << ": field '" << b->name << "' already has data (" << s << "). Overwriting.");
00294                                 }
00295                                 s = ParseFieldString(field);
00296                                 if( b->iconvNeeded && ic )
00297                                         s = ic->FromBB(s);
00298                                 return links;
00299                         }
00300                         else if( b->timeMember && btohs(field->size) == 4 ) {
00301                                 TimeT &t = rec->*(b->timeMember);
00302                                 t.Time = min2time(field->u.min1900);
00303                                 return links;
00304                         }
00305                 }
00306         }
00307         return 0;
00308 }
00309 
00310 template <class RecordT>
00311 FieldLink<RecordT>* ParseFieldByTable(RecordT *rec,
00312                                       const CommonField *field,
00313                                       const IConverter *ic,
00314                                       FieldLink<RecordT> **b)
00315 {
00316         for( ; *b; b++ ) {
00317                 FieldLink<RecordT> *link =
00318                         ParseFieldByTable<RecordT>(rec, field, ic, *b);
00319                 if( link )
00320                         return link;
00321         }
00322         return 0;
00323 }
00324 
00325 ServiceBook::ServiceBook()
00326         : m_data( new ServiceBookData(ServiceBookOldFieldLinks) )
00327         , RecordId(0)
00328 {
00329         Clear();
00330 }
00331 
00332 ServiceBook::~ServiceBook()
00333 {
00334 }
00335 
00336 const unsigned char* ServiceBook::ParseField(const unsigned char *begin,
00337                                              const unsigned char *end,
00338                                              const IConverter *ic)
00339 {
00340         const CommonField *field = (const CommonField *) begin;
00341 
00342         // advance and check size
00343         begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
00344         if( begin > end )               // if begin==end, we are ok
00345                 return begin;
00346 
00347         if( !btohs(field->size) )       // if field has no size, something's up
00348                 return begin;
00349 
00350         // cycle through the type tables
00351         FieldLink<ServiceBook> *typeSet =
00352                 ParseFieldByTable(this, field, ic, ServiceBookLinkTable);
00353         if( typeSet ) {
00354                 if( m_data->m_typeSet && m_data->m_typeSet != typeSet ) {
00355                         dout("ServiceBook record has a mix of old and new field types.");
00356                 }
00357                 m_data->m_typeSet = typeSet;
00358                 return begin;
00359         }
00360         else {
00361                 if( ParseFieldByTable(this, field, ic, ServiceBookFieldLinks) )
00362                         return begin;   // done!
00363         }
00364 
00365         // handle special cases
00366         switch( field->type )
00367         {
00368         case SBFC_CONFIG:
00369                 try {
00370                         Data config((const void *)field->u.raw, btohs(field->size));
00371                         size_t offset = 0;
00372                         Config.ParseHeader(config, offset);
00373                         Config.ParseFields(config, offset);
00374                         return begin;   // success
00375                 }
00376                 catch( BadPackedFormat & ) {
00377                         // break here so unprocessed raw packet is still
00378                         // visible in dump
00379                         break;
00380                 }
00381         }
00382 
00383         // if still not handled, add to the Unknowns list
00384         UnknownField uf;
00385         uf.type = field->type;
00386         uf.data.assign((const char*)field->u.raw, btohs(field->size));
00387         Unknowns.push_back(uf);
00388 
00389         // return new pointer for next field
00390         return begin;
00391 }
00392 
00393 void ServiceBook::ParseHeader(const Data &data, size_t &offset)
00394 {
00395         // no header in this record (?)
00396 }
00397 
00398 void ServiceBook::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
00399 {
00400         const unsigned char *finish = ParseCommonFields(*this,
00401                 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
00402         offset += finish - (data.GetData() + offset);
00403 }
00404 
00405 void ServiceBook::Validate() const
00406 {
00407         Config.Validate();
00408 }
00409 
00410 void ServiceBook::BuildHeader(Data &data, size_t &offset) const
00411 {
00412         // no header in this record (?)
00413 }
00414 
00415 //
00416 // BuildFields
00417 //
00418 /// Build fields part of record
00419 ///
00420 void ServiceBook::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
00421 {
00422         throw std::logic_error("ServiceBook::BuildFields not yet implemented");
00423 }
00424 
00425 void ServiceBook::Clear()
00426 {
00427         m_data->m_typeSet = ServiceBookOldFieldLinks;
00428         Unknowns.clear();
00429         Config.Clear();
00430 }
00431 
00432 const FieldHandle<ServiceBook>::ListT& ServiceBook::GetFieldHandles()
00433 {
00434         static FieldHandle<ServiceBook>::ListT fhv;
00435 
00436         if( fhv.size() )
00437                 return fhv;
00438 
00439 #undef CONTAINER_OBJECT_NAME
00440 #define CONTAINER_OBJECT_NAME fhv
00441 
00442 #undef RECORD_CLASS_NAME
00443 #define RECORD_CLASS_NAME ServiceBook
00444 
00445         FHP(RecType, "Record Type Code");
00446         FHP(RecordId, "Unique Record ID");
00447 
00448         FHP(Name, "Name");
00449         FHP(HiddenName, "Hidden Name");
00450         FHP(Description, "Description");
00451         FHP(DSID, "DSID");
00452         FHP(BesDomain, "BES Domain");
00453         FHP(UniqueId, "Unique ID");
00454         FHP(ContentId, "Content ID");
00455 
00456         // FIXME - this config is not yet implmented fully... when it is,
00457         // will need to add this as a field to FieldHandle<>, and maybe
00458         // implement a ServiceBookConfig::GetFieldHandles()
00459         //ServiceBookConfig Config;
00460 
00461         FHP(Unknowns, "Unknown Fields");
00462 
00463         return fhv;
00464 }
00465 
00466 std::string ServiceBook::GetDescription() const
00467 {
00468         return Name;
00469 }
00470 
00471 inline void FormatStr(std::ostream &os, const char *name, const std::string &str)
00472 {
00473         ios_format_state state(os);
00474 
00475         if( str.size() ) {
00476                 os << "   " << setw(20) << name;
00477                 os << ": " << str << "\n";
00478         }
00479 }
00480 
00481 void ServiceBook::Dump(std::ostream &os) const
00482 {
00483         ios_format_state state(os);
00484 
00485         os.setf(ios::left);
00486         os.fill(' ');
00487 
00488         os << "ServiceBook entry: 0x" << setbase(16) << RecordId
00489                 << " (" << (unsigned int)RecType << ")\n";
00490 
00491         FormatStr(os, "Name", Name);
00492         FormatStr(os, "Hidden Name", HiddenName);
00493         FormatStr(os, "Description", Description);
00494         FormatStr(os, "DSID", DSID);
00495         FormatStr(os, "Unique ID", UniqueId);
00496         FormatStr(os, "Content ID", ContentId);
00497         FormatStr(os, "(BES) Domain", BesDomain);
00498 
00499         os << Config;
00500 
00501         // print any unknowns
00502         os << Unknowns;
00503 }
00504 
00505 bool ServiceBook::operator<(const ServiceBook &other) const
00506 {
00507         int cmp = BesDomain.compare(other.BesDomain);
00508         if( cmp == 0 )
00509                 cmp = DSID.compare(other.DSID);
00510         if( cmp == 0 )
00511                 cmp = Name.compare(other.Name);
00512         if( cmp == 0 )
00513                 cmp = UniqueId.compare(other.UniqueId);
00514         return cmp < 0;
00515 }
00516 
00517 } // namespace Barry
00518