- changed PKG_CONFIG_PATH setting in rpm script, so that systems
[barry.git] / src / record.cc
blob42df620fddf12ad0aa62571bf00d5d323e916863
1 ///
2 /// \file record.cc
3 /// Blackberry database record classes. Help translate data
4 /// from data packets to useful structurs, and back.
5 /// This header provides the common types and classes
6 /// used by the general record parser classes in the
7 /// r_*.h files. Only application-safe API stuff goes in
8 /// here. Internal library types go in record-internal.h
9 ///
12 Copyright (C) 2005-2007, Net Direct Inc. (http://www.netdirect.ca/)
14 This program is free software; you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by
16 the Free Software Foundation; either version 2 of the License, or
17 (at your option) any later version.
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
23 See the GNU General Public License in the COPYING file at the
24 root directory of this project for more details.
27 #include "record.h"
28 #include "record-internal.h"
29 #include "protocol.h"
30 #include "protostructs.h"
31 #include "data.h"
32 #include "time.h"
33 #include "error.h"
34 #include "endian.h"
35 #include <ostream>
36 #include <iomanip>
37 #include <time.h>
38 #include <stdexcept>
40 #define __DEBUG_MODE__
41 #include "debug.h"
43 using namespace std;
44 using namespace Barry::Protocol;
46 namespace Barry {
48 //////////////////////////////////////////////////////////////////////////////
49 // Field builder helper functions
51 void BuildField1900(Data &data, size_t &size, uint8_t type, time_t t)
53 size_t timesize = COMMON_FIELD_MIN1900_SIZE;
54 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + timesize;
55 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
56 CommonField *field = (CommonField *) pd;
58 field->size = htobs(timesize);
59 field->type = type;
60 field->u.min1900 = time2min(t);
62 size += fieldsize;
65 void BuildField(Data &data, size_t &size, uint8_t type, char c)
67 size_t strsize = 1;
68 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
69 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
70 CommonField *field = (CommonField *) pd;
72 field->size = htobs(strsize);
73 field->type = type;
74 memcpy(field->u.raw, &c, strsize);
76 size += fieldsize;
79 void BuildField(Data &data, size_t &size, uint8_t type, const std::string &str)
81 // include null terminator
82 size_t strsize = str.size() + 1;
83 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + strsize;
84 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
85 CommonField *field = (CommonField *) pd;
87 field->size = htobs(strsize);
88 field->type = type;
89 memcpy(field->u.raw, str.c_str(), strsize);
91 size += fieldsize;
94 void BuildField(Data &data, size_t &size, uint8_t type, const Barry::Protocol::GroupLink &link)
96 size_t linksize = sizeof(Barry::Protocol::GroupLink);
97 size_t fieldsize = COMMON_FIELD_HEADER_SIZE + linksize;
98 unsigned char *pd = data.GetBuffer(size + fieldsize) + size;
99 CommonField *field = (CommonField *) pd;
101 field->size = htobs(linksize);
102 field->type = type;
103 field->u.link = link;
105 size += fieldsize;
109 ///////////////////////////////////////////////////////////////////////////////
110 // CommandTable class
112 CommandTable::CommandTable()
116 CommandTable::~CommandTable()
120 const unsigned char* CommandTable::ParseField(const unsigned char *begin,
121 const unsigned char *end)
123 // check if there is enough data for a header
124 const unsigned char *headend = begin + sizeof(CommandTableField);
125 if( headend > end )
126 return headend;
128 const CommandTableField *field = (const CommandTableField *) begin;
130 // advance and check size
131 begin += COMMAND_FIELD_HEADER_SIZE + field->size; // size is byte
132 if( begin > end ) // if begin==end, we are ok
133 return begin;
135 if( !field->size ) // if field has no size, something's up
136 return begin;
138 Command command;
139 command.Code = field->code;
140 command.Name.assign((const char *)field->name, field->size);
141 Commands.push_back(command);
142 return begin;
145 void CommandTable::Parse(const Data &data, size_t offset)
147 if( offset >= data.GetSize() )
148 return;
150 const unsigned char *begin = data.GetData() + offset;
151 const unsigned char *end = data.GetData() + data.GetSize();
153 while( begin < end )
154 begin = ParseField(begin, end);
157 void CommandTable::Clear()
159 Commands.clear();
162 unsigned int CommandTable::GetCommand(const std::string &name) const
164 CommandArrayType::const_iterator b = Commands.begin();
165 for( ; b != Commands.end(); b++ )
166 if( b->Name == name )
167 return b->Code;
168 return 0;
171 void CommandTable::Dump(std::ostream &os) const
173 CommandArrayType::const_iterator b = Commands.begin();
174 os << "Command table:\n";
175 for( ; b != Commands.end(); b++ ) {
176 os << " Command: 0x" << setbase(16) << b->Code
177 << " '" << b->Name << "'\n";
183 ///////////////////////////////////////////////////////////////////////////////
184 // RecordStateTable class
186 RecordStateTable::RecordStateTable()
187 : m_LastNewRecordId(1)
191 RecordStateTable::~RecordStateTable()
195 const unsigned char* RecordStateTable::ParseField(const unsigned char *begin,
196 const unsigned char *end)
198 const RecordStateTableField *field = (const RecordStateTableField *) begin;
200 // advance and check size
201 begin += sizeof(RecordStateTableField);
202 if( begin > end ) // if begin==end, we are ok
203 return begin;
205 State state;
206 state.Index = btohs(field->index);
207 state.RecordId = btohl(field->uniqueId);
208 state.Dirty = (field->flags & BARRY_RSTF_DIRTY) != 0;
209 state.RecType = field->rectype;
210 state.Unknown2.assign((const char*)field->unknown2, sizeof(field->unknown2));
211 StateMap[state.Index] = state;
213 return begin;
216 void RecordStateTable::Parse(const Data &data)
218 size_t offset = 12; // skipping the unknown 2 bytes at start
220 if( offset >= data.GetSize() )
221 return;
223 const unsigned char *begin = data.GetData() + offset;
224 const unsigned char *end = data.GetData() + data.GetSize();
226 while( begin < end )
227 begin = ParseField(begin, end);
230 void RecordStateTable::Clear()
232 StateMap.clear();
233 m_LastNewRecordId = 1;
236 // Searches the StateMap table for RecordId, and returns the "index"
237 // in the map if found. Returns true if found, false if not.
238 // pFoundIndex can be null if only the existence of the index is desired
239 bool RecordStateTable::GetIndex(uint32_t RecordId, IndexType *pFoundIndex) const
241 StateMapType::const_iterator i = StateMap.begin();
242 for( ; i != StateMap.end(); ++i ) {
243 if( i->second.RecordId == RecordId ) {
244 if( pFoundIndex )
245 *pFoundIndex = i->first;
246 return true;
249 return false;
252 // Generate a new RecordId that is not in the state table.
253 // Starts at 1 and keeps incrementing until a free one is found.
254 uint32_t RecordStateTable::MakeNewRecordId() const
256 // start with next Id
257 m_LastNewRecordId++;
259 // make sure it doesn't already exist
260 StateMapType::const_iterator i = StateMap.begin();
261 while( i != StateMap.end() ) {
262 if( m_LastNewRecordId == i->second.RecordId ) {
263 m_LastNewRecordId++; // try again
264 i = StateMap.begin(); // start over
266 else {
267 ++i; // next State
270 return m_LastNewRecordId;
273 void RecordStateTable::Dump(std::ostream &os) const
275 ios::fmtflags oldflags = os.setf(ios::right);
276 char fill = os.fill(' ');
277 bool bPrintAscii = Data::PrintAscii();
278 Data::PrintAscii(false);
280 os << " Index RecordId Dirty RecType" << endl;
281 os << "------- ---------- ----- -------" << endl;
283 StateMapType::const_iterator b, e = StateMap.end();
284 for( b = StateMap.begin(); b != e ; ++b ) {
285 const State &state = b->second;
287 os.fill(' ');
288 os << setbase(10) << setw(7) << state.Index;
289 os << " 0x" << setbase(16) << setfill('0') << setw(8) << state.RecordId;
290 os << " " << setfill(' ') << setw(5) << (state.Dirty ? "yes" : "no");
291 os << " 0x" << setbase(16) << setfill('0') << setw(2) << state.RecType;
292 os << " " << Data(state.Unknown2.data(), state.Unknown2.size());
295 // cleanup the stream
296 os.flags(oldflags);
297 os.fill(fill);
298 Data::PrintAscii(bPrintAscii);
303 ///////////////////////////////////////////////////////////////////////////////
304 // DatabaseDatabase class
306 DatabaseDatabase::DatabaseDatabase()
310 DatabaseDatabase::~DatabaseDatabase()
314 template <class RecordType, class FieldType>
315 void DatabaseDatabase::ParseRec(const RecordType &rec, const unsigned char *end)
319 template <class FieldType>
320 const unsigned char* DatabaseDatabase::ParseField(const unsigned char *begin,
321 const unsigned char *end)
323 // check if there is enough data for a header
324 const unsigned char *headend = begin + sizeof(FieldType);
325 if( headend > end )
326 return headend;
328 // get our header
329 const FieldType *field = (const FieldType *) begin;
331 // advance and check size
332 begin += sizeof(FieldType) - sizeof(field->name) + ConvertHtoB(field->nameSize);
333 if( begin > end ) // if begin==end, we are ok
334 return begin;
336 if( !ConvertHtoB(field->nameSize) ) // if field has no size, something's up
337 return begin;
339 Database db;
340 db.Number = ConvertHtoB(field->dbNumber);
341 db.RecordCount = ConvertHtoB(field->dbRecordCount);
342 db.Name.assign((const char *)field->name, ConvertHtoB(field->nameSize) - 1);
343 Databases.push_back(db);
344 return begin;
347 void DatabaseDatabase::Parse(const Data &data)
349 // check size to make sure we have up to the DBAccess operation byte
350 if( data.GetSize() < (SB_PACKET_DBACCESS_HEADER_SIZE + 1) )
351 return;
353 MAKE_PACKET(pack, data);
354 const unsigned char *begin = 0;
355 const unsigned char *end = data.GetData() + data.GetSize();
357 switch( pack->u.db.u.response.operation )
359 case SB_DBOP_GET_DBDB:
360 // using the new protocol
361 if( data.GetSize() > SB_PACKET_DBDB_HEADER_SIZE ) {
362 begin = (const unsigned char *)
363 &pack->u.db.u.response.u.dbdb.field[0];
365 // this while check is ok, since ParseField checks
366 // for header size
367 while( begin < end )
368 begin = ParseField<DBDBField>(begin, end);
370 else
371 dout("DatabaseDatabase: not enough data for parsing");
372 break;
374 case SB_DBOP_OLD_GET_DBDB:
375 // using the old protocol
376 if( data.GetSize() > SB_PACKET_OLD_DBDB_HEADER_SIZE ) {
377 begin = (const unsigned char *)
378 &pack->u.db.u.response.u.old_dbdb.field[0];
380 // this while check is ok, since ParseField checks
381 // for header size
382 while( begin < end )
383 begin = ParseField<OldDBDBField>(begin, end);
385 else
386 dout("DatabaseDatabase: not enough data for parsing");
387 break;
389 default:
390 // unknown protocol
391 dout("Unknown protocol");
392 break;
398 void DatabaseDatabase::Clear()
400 Databases.clear();
403 bool DatabaseDatabase::GetDBNumber(const std::string &name,
404 unsigned int &number) const
406 DatabaseArrayType::const_iterator b = Databases.begin();
407 for( ; b != Databases.end(); b++ )
408 if( b->Name == name ) {
409 number = b->Number;
410 return true;
412 return false;
415 bool DatabaseDatabase::GetDBName(unsigned int number,
416 std::string &name) const
418 DatabaseArrayType::const_iterator b = Databases.begin();
419 for( ; b != Databases.end(); b++ )
420 if( b->Number == number ) {
421 name = b->Name;
422 return true;
424 return false;
427 void DatabaseDatabase::Dump(std::ostream &os) const
429 DatabaseArrayType::const_iterator b = Databases.begin();
430 os << "Database database:\n";
431 for( ; b != Databases.end(); b++ ) {
432 os << " Database: 0x" << setbase(16) << b->Number
433 << " '" << b->Name << "' (records: "
434 << setbase(10) << b->RecordCount << ")\n";
439 std::ostream& operator<< (std::ostream &os, const std::vector<UnknownField> &unknowns)
441 std::vector<UnknownField>::const_iterator
442 ub = unknowns.begin(), ue = unknowns.end();
443 if( ub != ue )
444 os << " Unknowns:\n";
445 for( ; ub != ue; ub++ ) {
446 os << " Type: 0x" << setbase(16)
447 << (unsigned int) ub->type
448 << " Data:\n" << Data(ub->data.data(), ub->data.size());
450 return os;
453 } // namespace Barry
456 #ifdef __TEST_MODE__
458 #include <iostream>
460 int main(int argc, char *argv[])
462 if( argc < 2 ) {
463 cerr << "Usage: test <datafile>" << endl;
464 return 1;
467 std::vector<Data> array;
468 if( !LoadDataArray(argv[1], array) ) {
469 cerr << "Unable to load file: " << argv[1] << endl;
470 return 1;
473 cout << "Loaded " << array.size() << " items" << endl;
475 for( std::vector<Data>::iterator b = array.begin(), e = array.end();
476 b != e; b++ )
478 Data &d = *b;
479 // cout << d << endl;
480 if( d.GetSize() > 13 && d.GetData()[6] == 0x4f ) {
481 Barry::Contact contact;
482 size_t size = 13;
483 contact.ParseFields(d, size);
484 cout << contact << endl;
485 contact.DumpLdif(cout, "ou=People,dc=example,dc=com");
487 else if( d.GetSize() > 13 && d.GetData()[6] == 0x44 ) {
488 Barry::Calendar cal;
489 size_t size = 13;
490 cal.ParseFields(d, size);
491 cout << cal << endl;
496 #endif