TODO update
[barry.git] / tools / btool.cc
blob2fa3a73d0afe4f591bdaaaddb5e270a1b3878af1
1 ///
2 /// \file btool.cc
3 /// Barry library tester
4 ///
6 /*
7 Copyright (C) 2005-2007, 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 <barry/barry.h>
23 #include <iomanip>
24 #include <iostream>
25 #include <fstream>
26 #include <vector>
27 #include <string>
28 #include <getopt.h>
31 using namespace std;
32 using namespace Barry;
34 void Usage()
36 cerr
37 << "btool - Command line USB Blackberry Test Tool\n"
38 << " Copyright 2005-2007, Net Direct Inc. (http://www.netdirect.ca/)\n\n"
39 << " -c dn Convert address book database to LDIF format, using the\n"
40 << " specified baseDN\n"
41 << " -d db Load database 'db' and dump to screen\n"
42 << " Can be used multiple times to fetch more than one DB\n"
43 #ifdef __BARRY_BOOST_MODE__
44 << " -f file Filename to save or load handheld data to/from\n"
45 #endif
46 << " -h This help\n"
47 << " -l List devices\n"
48 << " -p pin PIN of device to talk with\n"
49 << " If only one device plugged in, this flag is optional\n"
50 << " -s db Save database 'db' from data loaded from -f file\n"
51 << " -t Show database database table\n"
52 << " -T db Show record state table for given database\n"
53 << " -v Dump protocol data during operation\n"
54 << "\n"
55 << " -d Command modifiers: (can be used multiple times for more than 1 record)\n"
56 << "\n"
57 << " -r # Record index number as seen in the -T state table.\n"
58 << " This overrides the default -d behaviour, and only\n"
59 << " downloads the one specified record, sending to stdout.\n"
60 << " -R # Same as -r, but also clears the record's dirty flags.\n"
61 << " -D # Record index number as seen in the -T state table,\n"
62 << " which indicates the record to delete. Used with the -d\n"
63 << " command to specify the database.\n"
64 << endl;
67 class Contact2Ldif
69 std::string m_baseDN;
70 public:
71 Contact2Ldif(const std::string &baseDN) : m_baseDN(baseDN) {}
72 void operator()(const Contact &rec)
74 rec.DumpLdif(cout, m_baseDN);
78 template <class Record>
79 struct Store
81 std::vector<Record> records;
82 mutable typename std::vector<Record>::const_iterator rec_it;
83 std::string filename;
84 bool load;
85 int count;
87 Store(const string &filename, bool load)
88 : rec_it(records.end()),
89 filename(filename),
90 load(load),
91 count(0)
93 #ifdef __BARRY_BOOST_MODE__
94 try {
96 if( load && filename.size() ) {
97 // filename is available, attempt to load
98 cout << "Loading: " << filename << endl;
99 ifstream ifs(filename.c_str());
100 boost::archive::text_iarchive ia(ifs);
101 ia >> records;
102 cout << records.size()
103 << " records loaded from '"
104 << filename << "'" << endl;
105 sort(records.begin(), records.end());
106 rec_it = records.begin();
108 // debugging aid
109 typename std::vector<Record>::const_iterator beg = records.begin(), end = records.end();
110 for( ; beg != end; beg++ ) {
111 cout << (*beg) << endl;
115 } catch( boost::archive::archive_exception &ae ) {
116 cerr << "Archive exception in ~Store(): "
117 << ae.what() << endl;
119 #endif
121 ~Store()
123 cout << "Store counted " << dec << count << " records." << endl;
124 #ifdef __BARRY_BOOST_MODE__
125 try {
127 if( !load && filename.size() ) {
128 // filename is available, attempt to save
129 cout << "Saving: " << filename << endl;
130 const std::vector<Record> &r = records;
131 ofstream ofs(filename.c_str());
132 boost::archive::text_oarchive oa(ofs);
133 oa << r;
134 cout << dec << r.size() << " records saved to '"
135 << filename << "'" << endl;
138 } catch( boost::archive::archive_exception &ae ) {
139 cerr << "Archive exception in ~Store(): "
140 << ae.what() << endl;
142 #endif
145 // storage operator
146 void operator()(const Record &rec)
148 count++;
149 std::cout << rec << std::endl;
150 records.push_back(rec);
153 // retrieval operator
154 bool operator()(Record &rec, unsigned int databaseId) const
156 if( rec_it == records.end() )
157 return false;
158 rec = *rec_it;
159 rec_it++;
160 return true;
164 class DataDumpParser : public Barry::Parser
166 uint32_t m_id;
168 public:
169 virtual void SetUniqueId(uint32_t id)
171 m_id = id;
174 virtual void ParseFields(const Barry::Data &data, size_t &offset)
176 std::cout << "Raw record dump for record: "
177 << std::hex << m_id << std::endl;
178 std::cout << data << std::endl;
182 auto_ptr<Parser> GetParser(const string &name, const string &filename)
184 // check for recognized database names
185 if( name == "Address Book" ) {
186 return auto_ptr<Parser>(
187 new RecordParser<Contact, Store<Contact> > (
188 new Store<Contact>(filename, false)));
190 else if( name == "Messages" ) {
191 return auto_ptr<Parser>(
192 new RecordParser<Message, Store<Message> > (
193 new Store<Message>(filename, false)));
195 else if( name == "Calendar" ) {
196 return auto_ptr<Parser>(
197 new RecordParser<Calendar, Store<Calendar> > (
198 new Store<Calendar>(filename, false)));
200 else if( name == "Service Book" ) {
201 return auto_ptr<Parser>(
202 new RecordParser<ServiceBook, Store<ServiceBook> > (
203 new Store<ServiceBook>(filename, false)));
205 else {
206 // unknown database, use null parser
207 return auto_ptr<Parser>( new DataDumpParser );
211 auto_ptr<Builder> GetBuilder(const string &name, const string &filename)
213 // check for recognized database names
214 if( name == "Address Book" ) {
215 return auto_ptr<Builder>(
216 new RecordBuilder<Contact, Store<Contact> > (
217 new Store<Contact>(filename, true)));
220 else if( name == "Messages" ) {
221 return auto_ptr<Parser>(
222 new RecordParser<Message, Store<Message> > (
223 new Store<Message>(filename, true)));
225 else if( name == "Calendar" ) {
226 return auto_ptr<Parser>(
227 new RecordParser<Calendar, Store<Calendar> > (
228 new Store<Calendar>(filename, true)));
230 else if( name == "Service Book" ) {
231 return auto_ptr<Parser>(
232 new RecordParser<ServiceBook, Store<ServiceBook> > (
233 new Store<ServiceBook>(filename, true)));
236 else {
237 throw std::runtime_error("No Builder available for database");
241 struct StateTableCommand
243 char flag;
244 bool clear;
245 unsigned int index;
247 StateTableCommand(char f, bool c, unsigned int i)
248 : flag(f), clear(c), index(i) {}
251 int main(int argc, char *argv[])
253 cout.sync_with_stdio(true); // leave this on, since libusb uses
254 // stdio for debug messages
256 try {
258 uint32_t pin = 0;
259 bool list_only = false,
260 show_dbdb = false,
261 ldif_contacts = false,
262 data_dump = false,
263 record_state = false;
264 string ldifBaseDN;
265 string filename;
266 vector<string> dbNames, saveDbNames;
267 vector<StateTableCommand> stCommands;
269 // process command line options
270 for(;;) {
271 int cmd = getopt(argc, argv, "c:d:D:f:hlp:r:R:s:tT:v");
272 if( cmd == -1 )
273 break;
275 switch( cmd )
277 case 'c': // contacts to ldap ldif
278 ldif_contacts = true;
279 ldifBaseDN = optarg;
280 break;
282 case 'd': // show dbname
283 dbNames.push_back(string(optarg));
284 break;
286 case 'D': // delete record
287 stCommands.push_back(
288 StateTableCommand('D', false, atoi(optarg)));
289 break;
291 case 'f': // filename
292 #ifdef __BARRY_BOOST_MODE__
293 filename = optarg;
294 #else
295 cerr << "-f option not supported - no Boost "
296 "serialization support available\n";
297 return 1;
298 #endif
299 break;
300 case 'l': // list only
301 list_only = true;
302 break;
304 case 'p': // Blackberry PIN
305 pin = strtoul(optarg, NULL, 16);
306 break;
308 case 'r': // get specific record index
309 stCommands.push_back(
310 StateTableCommand('r', false, atoi(optarg)));
311 break;
313 case 'R': // same as 'r', and clears dirty
314 stCommands.push_back(
315 StateTableCommand('r', true, atoi(optarg)));
316 break;
318 case 's': // save dbname
319 saveDbNames.push_back(string(optarg));
320 break;
322 case 't': // display database database
323 show_dbdb = true;
324 break;
326 case 'T': // show RecordStateTable
327 record_state = true;
328 dbNames.push_back(string(optarg));
329 break;
331 case 'v': // data dump on
332 data_dump = true;
333 break;
335 case 'h': // help
336 default:
337 Usage();
338 return 0;
342 // Initialize the barry library. Must be called before
343 // anything else.
344 Barry::Init(data_dump);
346 // Probe the USB bus for Blackberry devices and display.
347 // If user has specified a PIN, search for it in the
348 // available device list here as well
349 Barry::Probe probe;
350 int activeDevice = -1;
351 cout << "Blackberry devices found:" << endl;
352 for( int i = 0; i < probe.GetCount(); i++ ) {
353 cout << probe.Get(i) << endl;
354 if( probe.Get(i).m_pin == pin )
355 activeDevice = i;
358 if( list_only )
359 return 0; // done
361 if( activeDevice == -1 ) {
362 if( pin == 0 ) {
363 // can we default to single device?
364 if( probe.GetCount() == 1 )
365 activeDevice = 0;
366 else {
367 cerr << "No device selected" << endl;
368 return 1;
371 else {
372 cerr << "PIN " << setbase(16) << pin
373 << " not found" << endl;
374 return 1;
378 // Create our controller object
379 Barry::Controller con(probe.Get(activeDevice));
382 // execute each mode that was turned on
386 // Dump list of all databases to stdout
387 if( show_dbdb ) {
388 // open desktop mode socket
389 con.OpenMode(Controller::Desktop);
390 cout << con.GetDBDB() << endl;
393 // Dump list of contacts to an LDAP LDIF file
394 // This uses the Controller convenience templates
395 if( ldif_contacts ) {
396 // make sure we're in desktop mode
397 con.OpenMode(Controller::Desktop);
399 // create a storage functor object that accepts
400 // Barry::Contact objects as input
401 Contact2Ldif storage(ldifBaseDN);
403 // load all the Contact records into storage
404 con.LoadDatabaseByType<Barry::Contact>(storage);
407 // Dump record state table to stdout
408 if( record_state ) {
409 if( dbNames.size() == 0 ) {
410 cout << "No db names to process" << endl;
411 return 1;
414 vector<string>::iterator b = dbNames.begin();
415 for( ; b != dbNames.end(); b++ ) {
416 con.OpenMode(Controller::Desktop);
417 unsigned int id = con.GetDBID(*b);
418 RecordStateTable state;
419 con.GetRecordStateTable(id, state);
420 cout << "Record state table for: " << *b << endl;
421 cout << state;
423 return 0;
426 // Get Record mode overrides the default name mode
427 if( stCommands.size() ) {
428 if( dbNames.size() != 1 ) {
429 cout << "Must have 1 db name to process" << endl;
430 return 1;
433 con.OpenMode(Controller::Desktop);
434 unsigned int id = con.GetDBID(dbNames[0]);
435 auto_ptr<Parser> parse = GetParser(dbNames[0],filename);
437 for( unsigned int i = 0; i < stCommands.size(); i++ ) {
438 con.GetRecord(id, stCommands[i].index, *parse.get());
440 if( stCommands[i].flag == 'r' && stCommands[i].clear ) {
441 cout << "Clearing record's dirty flags..." << endl;
442 con.ClearDirty(id, stCommands[i].index);
445 if( stCommands[i].flag == 'D' ) {
446 con.DeleteRecord(id, stCommands[i].index);
450 return 0;
453 // Dump contents of selected databases to stdout, or
454 // to file if specified.
455 // This is retrieving data from the Blackberry.
456 if( dbNames.size() ) {
457 vector<string>::iterator b = dbNames.begin();
459 for( ; b != dbNames.end(); b++ ) {
460 con.OpenMode(Controller::Desktop);
461 auto_ptr<Parser> parse = GetParser(*b,filename);
462 unsigned int id = con.GetDBID(*b);
463 con.LoadDatabase(id, *parse.get());
467 // Save contents of file to specified databases
468 // This is writing data to the Blackberry.
469 if( saveDbNames.size() ) {
470 vector<string>::iterator b = saveDbNames.begin();
472 for( ; b != saveDbNames.end(); b++ ) {
473 con.OpenMode(Controller::Desktop);
474 auto_ptr<Builder> build =
475 GetBuilder(*b, filename);
476 unsigned int id = con.GetDBID(*b);
477 con.SaveDatabase(id, *build);
482 catch( Barry::BError &se ) {
483 std::cerr << "BError caught: " << se.what() << endl;
485 catch( Usb::UsbError &ue) {
486 std::cerr << "UsbError caught: " << ue.what() << endl;
488 catch( std::runtime_error &re ) {
489 std::cerr << "std::runtime_error caught: " << re.what() << endl;
490 return 1;
492 catch( std::exception &e ) {
493 std::cerr << "std::exception caught: " << e.what() << endl;
494 return 1;
497 return 0;