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
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.
28 #include "record-internal.h"
30 #include "protostructs.h"
40 #define __DEBUG_MODE__
44 using namespace Barry::Protocol
;
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
);
60 field
->u
.min1900
= time2min(t
);
65 void BuildField(Data
&data
, size_t &size
, uint8_t type
, char c
)
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
);
74 memcpy(field
->u
.raw
, &c
, strsize
);
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
);
89 memcpy(field
->u
.raw
, str
.c_str(), strsize
);
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
);
103 field
->u
.link
= link
;
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
);
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
135 if( !field
->size
) // if field has no size, something's up
139 command
.Code
= field
->code
;
140 command
.Name
.assign((const char *)field
->name
, field
->size
);
141 Commands
.push_back(command
);
145 void CommandTable::Parse(const Data
&data
, size_t offset
)
147 if( offset
>= data
.GetSize() )
150 const unsigned char *begin
= data
.GetData() + offset
;
151 const unsigned char *end
= data
.GetData() + data
.GetSize();
154 begin
= ParseField(begin
, end
);
157 void CommandTable::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
)
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
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
;
216 void RecordStateTable::Parse(const Data
&data
)
218 size_t offset
= 12; // skipping the unknown 2 bytes at start
220 if( offset
>= data
.GetSize() )
223 const unsigned char *begin
= data
.GetData() + offset
;
224 const unsigned char *end
= data
.GetData() + data
.GetSize();
227 begin
= ParseField(begin
, end
);
230 void RecordStateTable::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
) {
245 *pFoundIndex
= i
->first
;
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
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
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
;
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
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
);
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
336 if( !ConvertHtoB(field
->nameSize
) ) // if field has no size, something's up
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
);
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) )
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
368 begin
= ParseField
<DBDBField
>(begin
, end
);
371 dout("DatabaseDatabase: not enough data for parsing");
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
383 begin
= ParseField
<OldDBDBField
>(begin
, end
);
386 dout("DatabaseDatabase: not enough data for parsing");
391 dout("Unknown protocol");
398 void DatabaseDatabase::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
) {
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
) {
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();
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());
460 int main(int argc
, char *argv
[])
463 cerr
<< "Usage: test <datafile>" << endl
;
467 std::vector
<Data
> array
;
468 if( !LoadDataArray(argv
[1], array
) ) {
469 cerr
<< "Unable to load file: " << argv
[1] << endl
;
473 cout
<< "Loaded " << array
.size() << " items" << endl
;
475 for( std::vector
<Data
>::iterator b
= array
.begin(), e
= array
.end();
479 // cout << d << endl;
480 if( d
.GetSize() > 13 && d
.GetData()[6] == 0x4f ) {
481 Barry::Contact contact
;
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 ) {
490 cal
.ParseFields(d
, size
);