test: added nightly build test script
[barry/progweb.git] / doc / Hacking
blobecfa9de9559ed8cbf776b3e4a96222bca7bed95b
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;
97         union DBData
98         {
99                 DBCommand               command;
100                 DBResponse              response;
101                 CommandTableField       table[1];
102                 uint8_t                 return_code;
103                 uint8_t                 fragment[1];
105         } __attribute__ ((packed)) u;
106 } __attribute__ ((packed));
110 When downloading database records, they all arrive in a DBResponse packet.
112 struct DBResponse
114         uint8_t         operation;
116         union Parameters
117         {
119                 DBR_OldTaggedRecord     tagged;
120                 DBR_OldDBDBRecord       old_dbdb;
121                 DBR_DBDBRecord          dbdb;
123         } __attribute__ ((packed)) u;
125 } __attribute__ ((packed));
128 Depending on the command you use, the format of the record will change.
129 Also, newer versions of Blackberries have new "GET" commands that
130 return a slightly different record header.  Barry currenly uses
131 the "old" command, which is supported by all USB Blackberries tested
132 so far, so it has the greatest compatibility.
134 Some database records are tagged with a unique ID.  These databases
135 include the Contact records, Service Book records, and Calendar
136 records.  This allows for intelligent syncing, as Barry can reference,
137 update, and delete records individually instead of a group.
139 struct DBR_OldTaggedRecord
141         uint8_t         unknown;
142         uint16_t        index;
143         uint32_t        uniqueId;
144         uint8_t         unknown2;
146         union TaggedData
147         {
148                 CommonField     field[1];
149         } __attribute__ ((packed)) u;
150 } __attribute__ ((packed));
154 This is enough information to get you to the data you need to reverse
155 engineer.  That data will be located at:
157         packet->u.db.u.response.u.tagged.u.field[0]
160 So, taking an Address Book record as an example:
162     00000000: 06 00 37 00 40 03 44 00 08 00 80 82 b1 22 00 06  ..7.@.D......"..
163     00000010: 00 20 43 68 72 69 73 00 05 00 20 46 72 65 79 00  . Chris... Frey.
164     00000020: 14 00 01 63 64 66 72 65 79 40 6e 65 74 64 69 72  ...cdfrey@netdir
165     00000030: 65 63 74 2e 63 61 00                             ect.ca.
167 We now know:
169         06 00           - packet->socket
170         37 00           - packet->size
171         40              - packet->command
172         03              - packet->u.db.tableCmd
173         44              - packet->u.db.u.response.operation
174         00              - packet->u.db.u.response.u.tagged.unknown
175         08 00           - packet->u.db.u.response.u.tagged.index
176         80 82 b1 22     - packet->u.db.u.response.u.tagged.uniqueId
177         00              - packet->u.db.u.response.u.tagged.unknown2
178         06 00           - packet->u.db.u.response.u.tagged.u.field[0].size
179         20              - packet->u.db.u.response.u.tagged.u.field[0].type
180         ...             - continued data from sequential CommonField packets
182 Note that CommonField packets are variable size, so you can only use
183 field[0] the first time.  You'll need to do pointer arithmetic to
184 calculate the start of the next CommonField.
186 This is a common theme in the database records.  There is a static header
187 (eg. DBR_OldTaggedRecord) followed by a number of fields, which each contains
188 a common field header (eg. CommonField), followed by a variable sized block
189 of data (CommonField's u.raw field).  The next field starts size bytes later,
190 and so on.
194 This should give you enough information to begin hacking on additional
195 database records.  Once you have a "Record" header struct, you are ready
196 to begin parsing into an API class.
201 Record API Classes
202 ------------------
203 Record API Classes are intended to be consistent, C++-friendly structures
204 for storing the parsed data.  You can find them in:
206         src/record.h
208 These classes may be broken out into multiple files someday.
210 The requirements of these classes are as follows:
212         - no virtual functions
213         - must be valid STL container items
214         - generally contain all public data
215         - provide member functions for:
216                 - dumping to ostream
217                 - parsing downloaded data blocks from the BlackBerry
218                 - building data blocks for the BlackBerry for uploads
219         - must have absolutely no low level protocol contamination
221 The final point deserves some commentary.  When using gcc-specific extensions
222 like __attribute__, and when using pointer arithmetic and pointer casts
223 across types, as is necessary in some of the low level parsing code,
224 special compiler flags are required.
226 Specifically, g++ requires -fno-strict-aliasing to stop it from optimizing
227 away some of the pointer casts and data accesses.  This is not desireable
228 in an end-user application, so care must be taken to hide all low level
229 parsing in the library, while exposing the record class structures and
230 member function APIs to the application.
232 Therefore src/record.h (or any new record header you add) must not include
233 src/protostructs.h.  Your .cc code may include protostructs.h, but not
234 the header, which will be used by the application.
237 Good luck, and happy hacking!
239 October 2006