lib: fixed parsing of recurring VEVENTS: DAILY and interval support
[barry/progweb.git] / src / r_hhagent.cc
bloba88d3089a28516db6f62f97d07795851cfc19f74
1 ///
2 /// \file r_hhagent.cc
3 /// Blackberry database record parser class for Handheld Agent records
4 ///
6 /*
7 Copyright (C) 2011-2012, Net Direct Inc. (http://www.netdirect.ca/)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
22 #include "r_hhagent.h"
23 #include "record-internal.h"
24 #include "protostructs.h"
25 #include "iconv.h"
26 #include "time.h"
27 #include <iostream>
28 #include <sstream>
29 #include <iomanip>
30 #include <string>
31 #include "ios_state.h"
33 #define __DEBUG_MODE__
34 #include "debug.h"
36 using namespace std;
37 using namespace Barry::Protocol;
39 namespace Barry {
42 ///////////////////////////////////////////////////////////////////////////////
43 // HandheldAgent class
45 // Common field codes
46 #define HHAFC_END 0xffff
48 // Field codes for record 3000000
49 #define HHAFC3_MODEL 0x03
50 #define HHAFC3_BANDS 0x07
51 #define HHAFC3_VERSION 0x08
52 #define HHAFC3_NETWORK 0x0f
53 #define HHAFC3_PIN 0x10
54 #define HHAFC3_MEID 0x11
56 // Field codes for record 7000000
57 #define HHAFC7_FIRMWARE 0x13
58 #define HHAFC7_MANUFACTURER 0x14
59 #define HHAFC7_MODEL 0x15
60 #define HHAFC7_PLATFORM 0x17
62 // These fields are only valid for RecordId 0x3000000
63 static FieldLink<HandheldAgent> HandheldAgentFieldLinks_3000000[] = {
64 { HHAFC3_MODEL, "Model", 0, 0, &HandheldAgent::Model, 0, 0, 0, 0, true },
65 { HHAFC3_NETWORK, "Network", 0, 0, &HandheldAgent::Network, 0, 0, 0, 0, true },
66 { HHAFC3_BANDS, "Bands", 0, 0, &HandheldAgent::Bands, 0, 0, 0, 0, true },
67 { HHAFC3_MEID, "MEID/ESN", 0, 0, &HandheldAgent::MEID, 0, 0, 0, 0, true },
68 { HHAFC3_PIN, "PIN", 0, 0, &HandheldAgent::Pin, 0, 0, 0, 0, true },
69 { HHAFC3_VERSION, "Version",0, 0, &HandheldAgent::Version, 0, 0, 0, 0, true },
70 { HHAFC_END, "End of List",0, 0, 0, 0, 0, 0, 0, false }
73 // These fields are only for RecordId 0x4000000
74 static FieldLink<HandheldAgent> HandheldAgentFieldLinks_4000000[] = {
75 { HHAFC_END, "End of List",0, 0, 0, 0, 0, 0, 0, false }
78 // These fields are only for RecordId 0x5000000
79 static FieldLink<HandheldAgent> HandheldAgentFieldLinks_5000000[] = {
80 { HHAFC_END, "End of List",0, 0, 0, 0, 0, 0, 0, false }
83 // These fields are only for RecordId 0x7000000
84 static FieldLink<HandheldAgent> HandheldAgentFieldLinks_7000000[] = {
85 { HHAFC7_MODEL, "Model", 0, 0, &HandheldAgent::Model, 0, 0, 0, 0, true },
86 { HHAFC7_MANUFACTURER,"Manufacturer",0,0,&HandheldAgent::Manufacturer,0, 0, 0, 0, true },
87 { HHAFC7_FIRMWARE, "Firmware", 0, 0, &HandheldAgent::Version, 0, 0, 0, 0, true },
88 { HHAFC7_PLATFORM, "Platform", 0, 0, &HandheldAgent::PlatformVersion, 0, 0, 0, 0, true },
89 { HHAFC_END, "End of List",0, 0, 0, 0, 0, 0, 0, false }
92 // Use this table for default application style records
93 static FieldLink<HandheldAgent> HandheldAgentFieldLinks_Default[] = {
94 { HHAFC_END, "End of List",0, 0, 0, 0, 0, 0, 0, false }
97 // Use this for display / Dump() etc... includes all fields
98 static FieldLink<HandheldAgent> HandheldAgentFieldLinks_All[] = {
99 { 0, "Model", 0, 0, &HandheldAgent::Model, 0, 0, 0, 0, true },
100 { 0, "Network", 0, 0, &HandheldAgent::Network, 0, 0, 0, 0, true },
101 { 0, "Manufacturer",0,0, &HandheldAgent::Manufacturer,0, 0, 0, 0, true },
102 { 0, "Bands", 0, 0, &HandheldAgent::Bands, 0, 0, 0, 0, true },
103 { 0, "MEID/ESN", 0, 0, &HandheldAgent::MEID, 0, 0, 0, 0, true },
104 { 0, "PIN", 0, 0, &HandheldAgent::Pin, 0, 0, 0, 0, true },
105 { 0, "Version", 0, 0, &HandheldAgent::Version, 0, 0, 0, 0, true },
106 { 0, "Platform", 0, 0, &HandheldAgent::PlatformVersion, 0, 0, 0, 0, true },
107 { HHAFC_END, "End of List",0, 0, 0, 0, 0, 0, 0, false }
110 HandheldAgent::HandheldAgent()
112 Clear();
115 HandheldAgent::~HandheldAgent()
119 const unsigned char* HandheldAgent::ParseField(const unsigned char *begin,
120 const unsigned char *end,
121 const IConverter *ic)
123 const CommonField *field = (const CommonField *) begin;
125 // advance and check size
126 begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
127 if( begin > end ) // if begin==end, we are ok
128 return begin;
130 if( !btohs(field->size) ) // if field has no size, something's up
131 return begin;
133 // cycle through the type table
134 FieldLink<HandheldAgent> *b = HandheldAgentFieldLinks_Default;
135 if( RecordId == 0 ) {
136 // internal consistency check... all parsing code should
137 // call SetIds() first, and HandheldAgent relies on this,
138 // so double check, and throw if not
139 throw std::logic_error("HandheldAgent requires SetIds() to be called before ParseField()");
141 else if( RecordId == GetMEIDRecordId() ) {
142 b = HandheldAgentFieldLinks_3000000;
144 else if( RecordId == GetUnknown1RecordId() ) {
145 b = HandheldAgentFieldLinks_4000000;
147 else if( RecordId == GetUnknown2RecordId() ) {
148 b = HandheldAgentFieldLinks_5000000;
150 else if( RecordId == GetUnknown3RecordId() ) {
151 b = HandheldAgentFieldLinks_7000000;
154 for( ; b->type != HHAFC_END; b++ ) {
155 if( b->type == field->type ) {
156 if( b->strMember ) {
157 std::string &s = this->*(b->strMember);
158 s = ParseFieldString(field);
159 if( b->iconvNeeded && ic )
160 s = ic->FromBB(s);
161 return begin; // done!
163 else if( b->timeMember && btohs(field->size) == 4 ) {
164 TimeT &t = this->*(b->timeMember);
165 dout("min1900: " << field->u.min1900);
166 t.Time = min2time(field->u.min1900);
167 return begin;
169 else if( b->addrMember ) {
171 // parse email address
172 // get dual addr+name string first
173 // Note: this is a different format than
174 // used in r_message*.cc
176 std::string dual((const char*)field->u.raw, btohs(field->size));
178 EmailAddress a;
180 // assign first string, using null terminator
181 // letting std::string add it for us if it
182 // doesn't exist
183 a.Email = dual.c_str();
185 // assign second string, using first size
186 // as starting point
187 a.Name = dual.c_str() + a.Email.size() + 1;
189 // if the address is non-empty, add to list
190 if( a.size() ) {
191 // i18n convert if needed
192 if( b->iconvNeeded && ic ) {
193 a.Name = ic->FromBB(a.Name);
194 a.Email = ic->FromBB(a.Email);
197 EmailAddressList &al = this->*(b->addrMember);
198 al.push_back(a);
201 return begin;
206 // handle special cases
207 // switch( field->type )
208 // {
209 // }
211 // if still not handled, add to the Unknowns list
212 UnknownField uf;
213 uf.type = field->type;
214 uf.data.assign((const char*)field->u.raw, btohs(field->size));
215 Unknowns.push_back(uf);
217 // return new pointer for next field
218 return begin;
221 void HandheldAgent::ParseHeader(const Data &data, size_t &offset)
223 // no header in HandheldAgent records
226 void HandheldAgent::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
228 const unsigned char *finish = ParseCommonFields(*this,
229 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
230 offset += finish - (data.GetData() + offset);
233 void HandheldAgent::Validate() const
237 void HandheldAgent::BuildHeader(Data &data, size_t &offset) const
239 // no header in HandheldAgent records
243 // Build
245 /// Build fields part of record.
247 void HandheldAgent::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
251 void HandheldAgent::Clear()
253 // clear our fields
254 RecType = GetDefaultRecType();
255 RecordId = 0;
257 MEID.clear();
258 Model.clear();
259 Bands.clear();
260 Pin.clear();
261 Version.clear();
262 PlatformVersion.clear();
263 Manufacturer.clear();
264 Network.clear();
266 Unknowns.clear();
269 const FieldHandle<HandheldAgent>::ListT& HandheldAgent::GetFieldHandles()
271 static FieldHandle<HandheldAgent>::ListT fhv;
273 if( fhv.size() )
274 return fhv;
276 #undef CONTAINER_OBJECT_NAME
277 #define CONTAINER_OBJECT_NAME fhv
279 #undef RECORD_CLASS_NAME
280 #define RECORD_CLASS_NAME HandheldAgent
282 FHP(RecType, "Record Type Code");
283 FHP(RecordId, "Unique Record ID");
285 // These fields are only valid for RecordId 0x3000000
286 FHD(MEID, "MEID/ESN", HHAFC3_MEID, true);
287 FHD(Model, "Model", HHAFC3_MODEL, true);
288 FHD(Bands, "Bands", HHAFC3_BANDS, true);
289 FHD(Pin, "PIN", HHAFC3_PIN, true);
290 FHD(Version, "Version", HHAFC3_VERSION, true);
291 FHD(Network, "Network", HHAFC3_NETWORK, true);
293 // These fields are only for RecordId 0x7000000
294 FHD(PlatformVersion, "Platform Version", HHAFC7_PLATFORM, true);
295 FHD(Manufacturer, "Manufacturer", HHAFC7_MANUFACTURER, true);
297 FHP(Unknowns, "Unknown Fields");
299 return fhv;
302 std::string HandheldAgent::GetDescription() const
304 ostringstream oss;
305 oss << "Handheld Agent: 0x" << hex << RecordId;
306 return oss.str();
309 void HandheldAgent::Dump(std::ostream &os) const
311 ios_format_state state(os);
313 os << "HandheldAgent entry: 0x" << hex << RecordId
314 << " (" << (unsigned int)RecType << ")\n";
316 // cycle through the type table
317 for( const FieldLink<HandheldAgent> *b = HandheldAgentFieldLinks_All;
318 b->type != HHAFC_END;
319 b++ )
321 if( b->strMember ) {
322 const std::string &s = this->*(b->strMember);
323 if( s.size() )
324 os << " " << b->name << ": " << s << "\n";
326 else if( b->timeMember ) {
327 TimeT t = this->*(b->timeMember);
328 if( t.Time > 0 )
329 os << " " << b->name << ": " << t << "\n";
330 else
331 os << " " << b->name << ": disabled\n";
333 else if( b->addrMember ) {
334 const EmailAddressList &al = this->*(b->addrMember);
335 EmailAddressList::const_iterator lb = al.begin(), le = al.end();
337 for( ; lb != le; ++lb ) {
338 if( !lb->size() )
339 continue;
341 os << " " << b->name << ": " << *lb << "\n";
346 // print any unknowns
347 os << Unknowns;
350 bool HandheldAgent::operator<(const HandheldAgent &other) const
352 return RecordId < other.RecordId;
355 bool HandheldAgent::IsSpecial(uint32_t record_id)
357 return
358 record_id == GetMEIDRecordId() ||
359 record_id == GetUnknown1RecordId() ||
360 record_id == GetUnknown2RecordId() ||
361 record_id == GetUnknown3RecordId();
365 // The ESN number is in two parts. When in decimal, the first 3
366 // characters are one number, then the rest. In hex, the first 2
367 // digits are the same number, then the rest. Both are padded
368 // with zeros.
370 // For example, hex: 4c070068 dec: 07600458856
371 // hex: [4c]070068 dec: [076]00458856
373 // Returns an empty string on error.
376 bool HandheldAgent::IsESNHex(const std::string &esn)
378 const char *hex = "0123456789ABCDEFabcdef";
379 size_t npos = string::npos;
381 if( esn.size() == 8 && esn.find_first_not_of(hex) == npos ) {
382 return true;
385 return false;
388 bool HandheldAgent::IsESNDec(const std::string &esn)
390 const char *dec = "0123456789";
391 size_t npos = string::npos;
393 if( esn.size() == 11 && esn.find_first_not_of(dec) == npos ) {
394 return true;
397 return false;
400 std::string HandheldAgent::ESNDec2Hex(const std::string &esn)
402 string empty;
404 if( esn.size() != 11 )
405 return empty;
407 unsigned int part1, part2;
408 istringstream iss(esn.substr(0, 3));
409 iss >> dec >> part1;
410 if( !iss )
411 return empty;
412 iss.str(esn.substr(3));
413 iss.clear();
414 iss >> dec >> part2;
415 if( !iss )
416 return empty;
418 ostringstream oss;
419 oss << setfill('0') << setw(2) << hex << part1;
420 oss << setfill('0') << setw(6) << hex << part2;
421 if( !oss )
422 return empty;
423 return oss.str();
426 std::string HandheldAgent::ESNHex2Dec(const std::string &esn)
428 string empty;
430 if( esn.size() != 8 )
431 return empty;
433 unsigned int part1, part2;
434 istringstream iss(esn.substr(0, 2));
435 iss >> hex >> part1;
436 if( !iss )
437 return empty;
438 iss.str(esn.substr(2));
439 iss.clear();
440 iss >> hex >> part2;
441 if( !iss )
442 return empty;
444 ostringstream oss;
445 oss << setfill('0') << setw(3) << dec << part1;
446 oss << setfill('0') << setw(8) << dec << part2;
447 if( !oss )
448 return empty;
449 return oss.str();
452 } // namespace Barry