- updated time conversion calls to match opensync's latest SVN
[barry.git] / Hacking
blobcc6074d5212428fc33d016ddd094db068917575e
1 You want to help?  Excellent!
2 -----------------------------
3 Barry currently only supports about 3 database record formats.  A
4 database dump reveals over 50 different databases types, and you can help
5 code these.
7 Use the following command to dump the Database Database to stdout:
9         ./btool -t
11 Pick a database you are interested in, and run the following command to
12 dump the protocol to stdout:
14         ./btool -v -d "Memos"
17 Protocol Documentation:
18 -----------------------
19 Once you have the raw protocol dump, you need to reverse engineer it.
20 There are a number of places you can find documentation on this.
22 The original source that I started with was the Cassis project docs.
23 You can find them here:
25         http://off.net/cassis/protocol-description.html
27 While these docs are quite helpful, they only cover the serial protocol.
29 The USB protocol is more data-block based.  There are no checksums, since
30 USB provides a reliable data transfer.  The common protocol headers are
31 struct based, until you get to the variable sized data.  (This is also
32 struct based, but requires pointer arithmetic.)
34 See the following Barry source file for protocol structure information:
36         src/protostructs.h
39 Protostructs.h
40 --------------
41 These structures are all low level in nature, and MUST be packed.  You
42 will notice that every struct in this header is flagged with the gcc
43 extension "__attribute__ ((packed))", which makes it useful to apply
44 structure to raw protocol data.
46 You can build / deconstruct a protocol packet in this manner:
48 Imagine you have a packet of raw data in the following buffer:
50         char buff[4096];
52 You can apply the Packet struct to it directly:
54         struct Packet *packet = (struct Packet *) buff;
56 Looking at the structure, this gives you a lot of fixed-position data:
60 ///////////////////////////////////////////////////////////////////////////////
61 // Main packet struct
63 struct Packet
65         uint16_t        socket;         // socket ID... 0 is always there
66         uint16_t        size;           // total size of data packet
67         uint8_t         command;
69         union PacketData
70         {
72                 SocketCommand           socket;
73                 SequenceCommand         sequence;
74                 ModeSelectCommand       mode;
75                 DBAccess                db;
76                 uint8_t                 raw[1];
78         } __attribute__ ((packed)) u;
79 } __attribute__ ((packed));
83 In the case of database records, you are interested in the DBAccess union
84 member:
89 ///////////////////////////////////////////////////////////////////////////////
90 // Database access command structure
92 // even fragmented packets have a tableCmd
93 struct DBAccess
95         uint8_t         tableCmd;
96         union DBData
97         {
98                 DBCommand               command;
99                 DBResponse              response;
100                 OldDBResponse           old_response;
101                 UploadCommand           upload;
102                 CommandTableField       table[1];
103                 OldDBDBRecord           old_dbdb;
104                 DBDBRecord              dbdb;
105                 uint8_t                 return_code;
107                 uint8_t                 fragment[1];
109         } __attribute__ ((packed)) u;
110 } __attribute__ ((packed));
114 When downloading database records, they all arrive in either a DBResponse
115 or OldDBResponse, depending on the database access command you use.
116 Barry uses the old, serial-compatible command, so you're interested in:
119 struct OldDBResponse
121         uint8_t         operation;
122         uint8_t         unknown;
123         uint16_t        count;
124         uint8_t         data[1];
125 } __attribute__ ((packed));
129 This is enough information to get you to the data you need to reverse
130 engineer.  That data will be located at:
132         packet->u.db.u.old_response.data[0]
135 So, taking an Address Book record as an example:
137     00000000: 06 00 37 00 40 03 44 00 08 00 80 82 b1 22 00 06  ..7.@.D......"..
138     00000010: 00 20 43 68 72 69 73 00 05 00 20 46 72 65 79 00  . Chris... Frey.
139     00000020: 14 00 01 63 64 66 72 65 79 40 6e 65 74 64 69 72  ...cdfrey@netdir
140     00000030: 65 63 74 2e 63 61 00                             ect.ca.
142 We now know:
144         06 00           - packet->socket
145         37 00           - packet->size
146         40              - packet->command
147         03              - packet->u.db.tableCmd
148         44              - packet->u.db.u.old_response.operation
149         00              - packet->u.db.u.old_response.unknown
150         08 00           - packet->u.db.u.old_response.count
151         80 82 b1 22     - packet->u.db.u.old_response.data[0]...
152                         first bytes of record data
154 In the case of Address Book records, the struct is:
156 struct OldContactRecord
158         uint32_t        uniqueId;
159         uint8_t         unknown;
160         CommonField     field[1];
161 } __attribute__ ((packed));
163 struct CommonField
165         uint16_t        size;           // including null terminator
166         uint8_t         type;
168         union FieldData
169         {
170                 GroupLink       link;
171                 MessageAddress  addr;
172                 int32_t         min1900;
173                 uint8_t         raw[1];
175         } __attribute__ ((packed)) u;
176 } __attribute__ ((packed));
179 Which gives the following code:
181         struct OldContactRecord *con = (struct OldContactRecord *)
182                 &packet->u.db.u.old_response.data[0];
184         80 82 b1 22     - con->uniqueId
185         00              - con->unknown
186         06 00           - con->field[0].size
187         20              - con->field[0].type
188         43 68 72...     - con->field[0].raw  ("Chris..."
189                         Data of first field
192 This is a common theme in the database records.  There is a static header
193 (eg. OldContactRecord) followed by a number of fields, which each contains
194 a common field header (eg. CommonField), followed by a variable sized block
195 of data (CommonField's u.raw field).  The next field starts size bytes later,
196 so you _cannot_ use con->field[1], con->field[2], etc.  You must advance using
197 the size of the data.
201 This should give you enough information to begin hacking on additional
202 database records.  Once you have a "Record" header struct, you are ready
203 to begin parsing into an API class.
208 Record API Classes
209 ------------------
210 Record API Classes are intended to be consistent, C++-friendly structures
211 for storing the parsed data.  You can find them in:
213         src/record.h
215 These classes may be broken out into multiple files someday.
217 The requirements of these classes are as follows:
219         - no virtual functions
220         - must be valid STL container items
221         - generally contain all public data
222         - provide member functions for:
223                 - dumping to ostream
224                 - parsing downloaded data blocks from the BlackBerry
225                 - building data blocks for the BlackBerry for uploads
226         - must have absolutely no low level contamination
228 The final point deserves some commentary.  When using gcc-specific extensions
229 like __attribute__, and when using pointer arithmetic and pointer casts
230 across types, as is necessary in some of the low level parsing code,
231 special compiler flags are required.
233 Specifically, g++ requires -fno-strict-aliasing to stop it from optimizing
234 away some of the pointer casts and data accesses.  This is not desireable
235 in an end-user application, so care must be taken to hide all low level
236 parsing in the library, while exposing the record class structures and
237 member function APIs to the application.
239 Therefore src/record.h (or any new record header you add) must not include
240 src/protostructs.h.  Your .cc code may include protostructs.h, but not
241 the header, which will be used by the application.
244 Good luck, and happy hacking!
246 December 2005