Bumped copyright dates for 2013
[barry.git] / src / r_servicebook.cc
blob48dc7ee291063318c37370b149c9859420afa60b
1 ///
2 /// \file r_servicebook.cc
3 /// Blackberry database record parser class for
4 /// Service Book records.
5 ///
7 /*
8 Copyright (C) 2005-2013, Net Direct Inc. (http://www.netdirect.ca/)
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 See the GNU General Public License in the COPYING file at the
20 root directory of this project for more details.
23 #include "i18n.h"
24 #include "r_servicebook.h"
25 #include "record-internal.h"
26 #include "protocol.h"
27 #include "protostructs.h"
28 #include "data.h"
29 #include "time.h"
30 #include "error.h"
31 #include "endian.h"
32 #include "iconv.h"
33 #include <ostream>
34 #include <iomanip>
35 #include <time.h>
36 #include <stdexcept>
37 #include "ios_state.h"
39 #define __DEBUG_MODE__
40 #include "debug.h"
42 using namespace std;
43 using namespace Barry::Protocol;
45 namespace Barry {
47 ///////////////////////////////////////////////////////////////////////////////
48 // ServiceBookConfig class
50 // service book packed field codes
51 #define SBFCC_END 0xffff
53 static FieldLink<ServiceBookConfig> ServiceBookConfigFieldLinks[] = {
54 // { SBFC_DSID, "DSID", 0, 0, &ServiceBook::DSID, 0, 0 },
55 { SBFCC_END, N_("End of List"),0, 0, 0, 0, 0 }
58 ServiceBookConfig::ServiceBookConfig()
59 : Format(0)
61 Clear();
64 ServiceBookConfig::~ServiceBookConfig()
68 const unsigned char* ServiceBookConfig::ParseField(const unsigned char *begin,
69 const unsigned char *end,
70 const IConverter *ic)
72 const void *raw;
73 uint16_t size, type;
75 switch( Format )
77 case 0x01:
78 case 0x02:
80 const PackedField_02 *field = (const PackedField_02 *) begin;
81 raw = field->raw;
82 size = field->size;
83 type = field->type;
84 begin += PACKED_FIELD_02_HEADER_SIZE + size;
86 break;
88 case 0x10:
90 const PackedField_10 *field = (const PackedField_10 *) begin;
91 raw = field->raw;
92 size = field->size;
93 type = field->type;
94 begin += PACKED_FIELD_10_HEADER_SIZE + size;
96 break;
98 default:
99 dout("------> Unknown packed field format: 0x" << std::hex <<
100 (unsigned int) Format);
101 throw BadPackedFormat(Format);
102 return begin + 1;
106 // check size
107 if( begin > end ) // if begin==end, we are ok
108 return begin;
110 if( !size ) // if field has no size, something's up
111 return begin;
113 // cycle through the type table
114 for( FieldLink<ServiceBookConfig> *b = ServiceBookConfigFieldLinks;
115 b->type != SBFCC_END;
116 b++ )
118 if( b->type == type ) {
119 if( b->strMember ) {
120 std::string &s = this->*(b->strMember);
121 s = ParseFieldString(raw, size-1);
122 return begin; // done!
128 // handle special cases
129 switch( type )
134 // if still not handled, add to the Unknowns list
135 UnknownField uf;
136 uf.type = (uint8_t)type;
137 uf.data.assign((const char*)raw, size);
138 Unknowns.push_back(uf);
140 // return new pointer for next field
141 return begin;
144 void ServiceBookConfig::ParseHeader(const Data &data, size_t &offset)
146 MAKE_RECORD(const Barry::Protocol::ServiceBookConfigField, sbc, data, offset);
147 offset += SERVICE_BOOK_CONFIG_FIELD_HEADER_SIZE;
148 if( data.GetSize() >= offset ) { // size check!
149 Format = sbc->format;
153 void ServiceBookConfig::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
155 const unsigned char *finish = ParseCommonFields(*this,
156 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
157 offset += finish - (data.GetData() + offset);
160 void ServiceBookConfig::Validate() const
164 void ServiceBookConfig::BuildHeader(Data &data, size_t &offset) const
166 // make sure there is enough space
167 data.GetBuffer(offset + SERVICE_BOOK_CONFIG_FIELD_HEADER_SIZE);
169 MAKE_RECORD(Barry::Protocol::ServiceBookConfigField, sbc, data, offset);
170 sbc->format = Format;
172 offset += SERVICE_BOOK_CONFIG_FIELD_HEADER_SIZE;
176 // BuildFields
178 /// Build fields part of record
180 void ServiceBookConfig::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
182 throw std::logic_error(_("ServiceBookConfig::Build not yet implemented"));
185 void ServiceBookConfig::Clear()
187 Format = 0;
188 Unknowns.clear();
191 void ServiceBookConfig::Dump(std::ostream &os) const
193 ios_format_state state(os);
195 os << _(" ServiceBookConfig Format: ") << setbase(16) << (uint16_t)Format << "\n";
197 // cycle through the type table
198 for( const FieldLink<ServiceBookConfig> *b = ServiceBookConfigFieldLinks;
199 b->type != SBFCC_END;
200 b++ )
202 if( b->strMember ) {
203 const std::string &s = this->*(b->strMember);
204 if( s.size() )
205 os << " " << gettext(b->name) << ": " << s << "\n";
207 else if( b->timeMember ) {
208 TimeT t = this->*(b->timeMember);
209 if( t.Time> 0 )
210 os << " " << gettext(b->name) << ": " << t << "\n";
214 // print any unknowns
215 os << Unknowns;
216 os << _(" ------------------- End of Config Field\n");
220 ///////////////////////////////////////////////////////////////////////////////
221 // ServiceBook class
223 // service book field codes
224 #define SBFC_OLD_NAME 0x01
225 #define SBFC_HIDDEN_NAME 0x02
226 #define SBFC_NAME 0x03
227 #define SBFC_OLD_UNIQUE_ID 0x06
228 #define SBFC_UNIQUE_ID 0x07
229 #define SBFC_CONTENT_ID 0x08
230 #define SBFC_CONFIG 0x09
231 #define SBFC_OLD_DESC 0x32
232 #define SBFC_DESCRIPTION 0x0f
233 #define SBFC_DSID 0xa1
234 #define SBFC_BES_DOMAIN 0xa2
235 #define SBFC_USER_ID 0xa3
236 #define SBFC_END 0xffff
238 // private data class, containing internal structures
239 class ServiceBookData
241 public:
242 FieldLink<ServiceBook> *m_typeSet;
243 ServiceBookData(FieldLink<ServiceBook> *typeSet) : m_typeSet(typeSet) {}
246 // The Old/New tables contain the same fields, but use different
247 // type codes. Keeping them separate yet linked makes it possible
248 // to convert between old and new type codes, while hopefully keeping
249 // things generic.
250 static FieldLink<ServiceBook> ServiceBookOldFieldLinks[] = {
251 { SBFC_OLD_NAME, N_("Old Name"), 0, 0, &ServiceBook::Name, 0, 0, 0, 0, true },
252 { SBFC_OLD_DESC, N_("Old Desc"), 0, 0, &ServiceBook::Description, 0, 0, 0, 0, true },
253 { SBFC_OLD_UNIQUE_ID, N_("Old UniqueId"), 0, 0, &ServiceBook::UniqueId, 0, 0, 0, 0, false },
254 { SBFC_END, N_("End of List"), 0, 0, 0, 0, 0, 0, 0, false }
257 static FieldLink<ServiceBook> ServiceBookNewFieldLinks[] = {
258 { SBFC_NAME, N_("Name"), 0, 0, &ServiceBook::Name, 0, 0, 0, 0, true },
259 { SBFC_DESCRIPTION, N_("Description"), 0, 0, &ServiceBook::Description, 0, 0, 0, 0, true },
260 { SBFC_UNIQUE_ID, N_("UniqueId"), 0, 0, &ServiceBook::UniqueId, 0, 0, 0, 0, false },
261 { SBFC_END, N_("End of List"), 0, 0, 0, 0, 0, 0, 0, false }
264 // This table holds all
265 static FieldLink<ServiceBook> ServiceBookFieldLinks[] = {
266 { SBFC_HIDDEN_NAME, N_("Hidden Name"),0, 0, &ServiceBook::HiddenName, 0, 0, 0, 0, true },
267 { SBFC_DSID, N_("DSID"), 0, 0, &ServiceBook::DSID, 0, 0, 0, 0, false },
268 { SBFC_CONTENT_ID, N_("ContentId"), 0, 0, &ServiceBook::ContentId, 0, 0, 0, 0, false },
269 { SBFC_BES_DOMAIN, N_("BES Domain"), 0, 0, &ServiceBook::BesDomain, 0, 0, 0, 0, false },
270 { SBFC_END, N_("End of List"),0, 0, 0, 0, 0, 0, 0, false }
273 // Array of conflicting tables only
274 static FieldLink<ServiceBook> *ServiceBookLinkTable[] = {
275 ServiceBookOldFieldLinks,
276 ServiceBookNewFieldLinks,
280 #define FIELDLINK_END 0xffff
282 template <class RecordT>
283 FieldLink<RecordT>* ParseFieldByTable(RecordT *rec,
284 const CommonField *field,
285 const IConverter *ic,
286 FieldLink<RecordT> *links)
288 // cycle through the type table
289 for( FieldLink<RecordT> *b = links; b->type != FIELDLINK_END; b++ ) {
290 if( b->type == field->type ) {
291 if( b->strMember ) {
292 std::string &s = rec->*(b->strMember);
293 if( s.size() ) {
294 dout(RecordT::GetDBName() << ": field '" << b->name << "' already has data (" << s << "). Overwriting.");
296 s = ParseFieldString(field);
297 if( b->iconvNeeded && ic )
298 s = ic->FromBB(s);
299 return links;
301 else if( b->timeMember && btohs(field->size) == 4 ) {
302 TimeT &t = rec->*(b->timeMember);
303 t.Time = min2time(field->u.min1900);
304 return links;
308 return 0;
311 template <class RecordT>
312 FieldLink<RecordT>* ParseFieldByTable(RecordT *rec,
313 const CommonField *field,
314 const IConverter *ic,
315 FieldLink<RecordT> **b)
317 for( ; *b; b++ ) {
318 FieldLink<RecordT> *link =
319 ParseFieldByTable<RecordT>(rec, field, ic, *b);
320 if( link )
321 return link;
323 return 0;
326 ServiceBook::ServiceBook()
327 : m_data( new ServiceBookData(ServiceBookOldFieldLinks) )
328 , RecordId(0)
330 Clear();
333 ServiceBook::~ServiceBook()
337 const unsigned char* ServiceBook::ParseField(const unsigned char *begin,
338 const unsigned char *end,
339 const IConverter *ic)
341 const CommonField *field = (const CommonField *) begin;
343 // advance and check size
344 begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
345 if( begin > end ) // if begin==end, we are ok
346 return begin;
348 if( !btohs(field->size) ) // if field has no size, something's up
349 return begin;
351 // cycle through the type tables
352 FieldLink<ServiceBook> *typeSet =
353 ParseFieldByTable(this, field, ic, ServiceBookLinkTable);
354 if( typeSet ) {
355 if( m_data->m_typeSet && m_data->m_typeSet != typeSet ) {
356 dout("ServiceBook record has a mix of old and new field types.");
358 m_data->m_typeSet = typeSet;
359 return begin;
361 else {
362 if( ParseFieldByTable(this, field, ic, ServiceBookFieldLinks) )
363 return begin; // done!
366 // handle special cases
367 switch( field->type )
369 case SBFC_CONFIG:
370 try {
371 Data config((const void *)field->u.raw, btohs(field->size));
372 size_t offset = 0;
373 Config.ParseHeader(config, offset);
374 Config.ParseFields(config, offset);
375 return begin; // success
377 catch( BadPackedFormat & ) {
378 // break here so unprocessed raw packet is still
379 // visible in dump
380 break;
384 // if still not handled, add to the Unknowns list
385 UnknownField uf;
386 uf.type = field->type;
387 uf.data.assign((const char*)field->u.raw, btohs(field->size));
388 Unknowns.push_back(uf);
390 // return new pointer for next field
391 return begin;
394 void ServiceBook::ParseHeader(const Data &data, size_t &offset)
396 // no header in this record (?)
399 void ServiceBook::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
401 const unsigned char *finish = ParseCommonFields(*this,
402 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
403 offset += finish - (data.GetData() + offset);
406 void ServiceBook::Validate() const
408 Config.Validate();
411 void ServiceBook::BuildHeader(Data &data, size_t &offset) const
413 // no header in this record (?)
417 // BuildFields
419 /// Build fields part of record
421 void ServiceBook::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
423 throw std::logic_error(_("ServiceBook::BuildFields not yet implemented"));
426 void ServiceBook::Clear()
428 m_data->m_typeSet = ServiceBookOldFieldLinks;
429 Unknowns.clear();
430 Config.Clear();
433 const FieldHandle<ServiceBook>::ListT& ServiceBook::GetFieldHandles()
435 static FieldHandle<ServiceBook>::ListT fhv;
437 if( fhv.size() )
438 return fhv;
440 #undef CONTAINER_OBJECT_NAME
441 #define CONTAINER_OBJECT_NAME fhv
443 #undef RECORD_CLASS_NAME
444 #define RECORD_CLASS_NAME ServiceBook
446 FHP(RecType, _("Record Type Code"));
447 FHP(RecordId, _("Unique Record ID"));
449 FHP(Name, _("Name"));
450 FHP(HiddenName, _("Hidden Name"));
451 FHP(Description, _("Description"));
452 FHP(DSID, _("DSID"));
453 FHP(BesDomain, _("BES Domain"));
454 FHP(UniqueId, _("Unique ID"));
455 FHP(ContentId, _("Content ID"));
457 // FIXME - this config is not yet implmented fully... when it is,
458 // will need to add this as a field to FieldHandle<>, and maybe
459 // implement a ServiceBookConfig::GetFieldHandles()
460 //ServiceBookConfig Config;
462 FHP(Unknowns, _("Unknown Fields"));
464 return fhv;
467 std::string ServiceBook::GetDescription() const
469 return Name;
472 inline void FormatStr(std::ostream &os, const char *name, const std::string &str)
474 ios_format_state state(os);
476 if( str.size() ) {
477 os << " " << setw(20) << name;
478 os << ": " << str << "\n";
482 void ServiceBook::Dump(std::ostream &os) const
484 ios_format_state state(os);
486 os.setf(ios::left);
487 os.fill(' ');
489 os << _("ServiceBook entry: ") << "0x" << setbase(16) << RecordId
490 << " (" << (unsigned int)RecType << ")\n";
492 FormatStr(os, _("Name"), Name);
493 FormatStr(os, _("Hidden Name"), HiddenName);
494 FormatStr(os, _("Description"), Description);
495 FormatStr(os, _("DSID"), DSID);
496 FormatStr(os, _("Unique ID"), UniqueId);
497 FormatStr(os, _("Content ID"), ContentId);
498 FormatStr(os, _("(BES) Domain"), BesDomain);
500 os << Config;
502 // print any unknowns
503 os << Unknowns;
506 bool ServiceBook::operator<(const ServiceBook &other) const
508 int cmp = BesDomain.compare(other.BesDomain);
509 if( cmp == 0 )
510 cmp = DSID.compare(other.DSID);
511 if( cmp == 0 )
512 cmp = Name.compare(other.Name);
513 if( cmp == 0 )
514 cmp = UniqueId.compare(other.UniqueId);
515 return cmp < 0;
518 } // namespace Barry