- removed usb_set_configuration() check from bcharge.cc, since
[barry.git] / tools / btool.cc
blob658116d3153369eda08525c94c19125c158a2191
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 int major, minor;
37 const char *Version = Barry::Version(major, minor);
39 cerr
40 << "btool - Command line USB Blackberry Test Tool\n"
41 << " Copyright 2005-2007, Net Direct Inc. (http://www.netdirect.ca/)\n"
42 << " Using: " << Version << "\n"
43 << " Compiled "
44 #ifdef __BARRY_BOOST_MODE__
45 << "with"
46 #else
47 << "without"
48 #endif
49 << " Boost support\n"
50 << "\n"
51 << " -c dn Convert address book database to LDIF format, using the\n"
52 << " specified baseDN\n"
53 << " -C dnattr LDIF attribute name to use when building the FQDN\n"
54 << " Defaults to 'cn'\n"
55 << " -d db Load database 'db' FROM device and dump to screen\n"
56 << " Can be used multiple times to fetch more than one DB\n"
57 #ifdef __BARRY_BOOST_MODE__
58 << " -f file Filename to save or load handheld data to/from\n"
59 #endif
60 << " -h This help\n"
61 << " -l List devices\n"
62 << " -L List Contact field names\n"
63 << " -m Map LDIF name to Contact field / Unmap LDIF name\n"
64 << " Map: ldif,read,write - maps ldif to read/write Contact fields\n"
65 << " Unmap: ldif name alone\n"
66 << " -M List current LDIF mapping\n"
67 << " -p pin PIN of device to talk with\n"
68 << " If only one device plugged in, this flag is optional\n"
69 << " -P pass Simplistic method to specify device password\n"
70 << " -s db Save database 'db' TO device from data loaded from -f file\n"
71 << " -S Show list of supported database parsers\n"
72 << " -t Show database database table\n"
73 << " -T db Show record state table for given database\n"
74 << " -v Dump protocol data during operation\n"
75 << " -X Reset device\n"
76 << "\n"
77 << " -d Command modifiers: (can be used multiple times for more than 1 record)\n"
78 << "\n"
79 << " -r # Record index number as seen in the -T state table.\n"
80 << " This overrides the default -d behaviour, and only\n"
81 << " downloads the one specified record, sending to stdout.\n"
82 << " -R # Same as -r, but also clears the record's dirty flags.\n"
83 << " -D # Record index number as seen in the -T state table,\n"
84 << " which indicates the record to delete. Used with the -d\n"
85 << " command to specify the database.\n"
86 << endl;
89 class Contact2Ldif
91 public:
92 Barry::ContactLdif &ldif;
94 Contact2Ldif(Barry::ContactLdif &ldif) : ldif(ldif) {}
96 void operator()(const Contact &rec)
98 ldif.DumpLdif(cout, rec);
102 template <class Record>
103 struct Store
105 std::vector<Record> records;
106 mutable typename std::vector<Record>::const_iterator rec_it;
107 std::string filename;
108 bool load;
109 int count;
111 Store(const string &filename, bool load)
112 : rec_it(records.end()),
113 filename(filename),
114 load(load),
115 count(0)
117 #ifdef __BARRY_BOOST_MODE__
118 try {
120 if( load && filename.size() ) {
121 // filename is available, attempt to load
122 cout << "Loading: " << filename << endl;
123 ifstream ifs(filename.c_str());
124 boost::archive::text_iarchive ia(ifs);
125 ia >> records;
126 cout << records.size()
127 << " records loaded from '"
128 << filename << "'" << endl;
129 sort(records.begin(), records.end());
130 rec_it = records.begin();
132 // debugging aid
133 typename std::vector<Record>::const_iterator beg = records.begin(), end = records.end();
134 for( ; beg != end; beg++ ) {
135 cout << (*beg) << endl;
139 } catch( boost::archive::archive_exception &ae ) {
140 cerr << "Archive exception in ~Store(): "
141 << ae.what() << endl;
143 #endif
145 ~Store()
147 cout << "Store counted " << dec << count << " records." << endl;
148 #ifdef __BARRY_BOOST_MODE__
149 try {
151 if( !load && filename.size() ) {
152 // filename is available, attempt to save
153 cout << "Saving: " << filename << endl;
154 const std::vector<Record> &r = records;
155 ofstream ofs(filename.c_str());
156 boost::archive::text_oarchive oa(ofs);
157 oa << r;
158 cout << dec << r.size() << " records saved to '"
159 << filename << "'" << endl;
162 } catch( boost::archive::archive_exception &ae ) {
163 cerr << "Archive exception in ~Store(): "
164 << ae.what() << endl;
166 #endif
169 // storage operator
170 void operator()(const Record &rec)
172 count++;
173 std::cout << rec << std::endl;
174 records.push_back(rec);
177 // retrieval operator
178 bool operator()(Record &rec, unsigned int databaseId) const
180 if( rec_it == records.end() )
181 return false;
182 rec = *rec_it;
183 rec_it++;
184 return true;
188 class DataDumpParser : public Barry::Parser
190 uint32_t m_id;
192 public:
193 virtual void SetUniqueId(uint32_t id)
195 m_id = id;
198 virtual void ParseFields(const Barry::Data &data, size_t &offset)
200 std::cout << "Raw record dump for record: "
201 << std::hex << m_id << std::endl;
202 std::cout << data << std::endl;
206 auto_ptr<Parser> GetParser(const string &name, const string &filename)
208 // check for recognized database names
209 if( name == "Address Book" ) {
210 return auto_ptr<Parser>(
211 new RecordParser<Contact, Store<Contact> > (
212 new Store<Contact>(filename, false)));
214 else if( name == "Messages" ) {
215 return auto_ptr<Parser>(
216 new RecordParser<Message, Store<Message> > (
217 new Store<Message>(filename, false)));
219 else if( name == "Calendar" ) {
220 return auto_ptr<Parser>(
221 new RecordParser<Calendar, Store<Calendar> > (
222 new Store<Calendar>(filename, false)));
224 else if( name == "Service Book" ) {
225 return auto_ptr<Parser>(
226 new RecordParser<ServiceBook, Store<ServiceBook> > (
227 new Store<ServiceBook>(filename, false)));
230 else if( name == "Memos" ) {
231 return auto_ptr<Parser>(
232 new RecordParser<Memo, Store<Memo> > (
233 new Store<Memo>(filename, false)));
235 else if( name == "Tasks" ) {
236 return auto_ptr<Parser>(
237 new RecordParser<Task, Store<Task> > (
238 new Store<Task>(filename, false)));
240 else if( name == "PIN Messages" ) {
241 return auto_ptr<Parser>(
242 new RecordParser<PINMessage, Store<PINMessage> > (
243 new Store<PINMessage>(filename, false)));
245 else if( name == "Saved Email Messages" ) {
246 return auto_ptr<Parser>(
247 new RecordParser<SavedMessage, Store<SavedMessage> > (
248 new Store<SavedMessage>(filename, false)));
250 else if( name == "Folders" ) {
251 return auto_ptr<Parser>(
252 new RecordParser<Folder, Store<Folder> > (
253 new Store<Folder>(filename, false)));
255 else {
256 // unknown database, use null parser
257 return auto_ptr<Parser>( new DataDumpParser );
261 auto_ptr<Builder> GetBuilder(const string &name, const string &filename)
263 // check for recognized database names
264 if( name == "Address Book" ) {
265 return auto_ptr<Builder>(
266 new RecordBuilder<Contact, Store<Contact> > (
267 new Store<Contact>(filename, true)));
270 else if( name == "Messages" ) {
271 return auto_ptr<Parser>(
272 new RecordParser<Message, Store<Message> > (
273 new Store<Message>(filename, true)));
275 else if( name == "Calendar" ) {
276 return auto_ptr<Parser>(
277 new RecordParser<Calendar, Store<Calendar> > (
278 new Store<Calendar>(filename, true)));
280 else if( name == "Service Book" ) {
281 return auto_ptr<Parser>(
282 new RecordParser<ServiceBook, Store<ServiceBook> > (
283 new Store<ServiceBook>(filename, true)));
285 else if( name == "Memos" ) {
286 return auto_ptr<Parser>(
287 new RecordParser<Memo, Store<Memo> > (
288 new Store<Memo>(filename, true)));
290 else if( name == "Tasks" ) {
291 return auto_ptr<Parser>(
292 new RecordParser<Task, Store<Task> > (
293 new Store<Task>(filename, true)));
296 else {
297 throw std::runtime_error("No Builder available for database");
301 void ShowParsers()
303 cout << "Supported Database parsers:\n"
304 << " Address Book\n"
305 << " Messages\n"
306 << " Calendar\n"
307 << " Service Book\n"
308 << " Memos\n"
309 << " Tasks\n"
310 << " PIN Messages\n"
311 << " Saved Email Messages\n"
312 << " Folders\n"
313 << "\n"
314 << "Supported Database builders:\n"
315 << " Address Book\n"
316 << endl;
319 struct StateTableCommand
321 char flag;
322 bool clear;
323 unsigned int index;
325 StateTableCommand(char f, bool c, unsigned int i)
326 : flag(f), clear(c), index(i) {}
329 bool SplitMap(const string &map, string &ldif, string &read, string &write)
331 string::size_type a = map.find(',');
332 if( a == string::npos )
333 return false;
335 string::size_type b = map.find(',', a+1);
336 if( b == string::npos )
337 return false;
339 ldif.assign(map, 0, a);
340 read.assign(map, a + 1, b - a - 1);
341 write.assign(map, b + 1, map.size() - b - 1);
343 return ldif.size() && read.size() && write.size();
346 void DoMapping(ContactLdif &ldif, const vector<string> &mapCommands)
348 for( vector<string>::const_iterator i = mapCommands.begin();
349 i != mapCommands.end();
350 ++i )
352 // single names mean unmapping
353 if( i->find(',') == string::npos ) {
354 // unmap
355 cerr << "Unmapping: " << *i << endl;
356 ldif.Unmap(*i);
358 else {
359 cerr << "Mapping: " << *i << endl;
361 // map... extract ldif/read/write names
362 string ldifname, read, write;
363 if( SplitMap(*i, ldifname, read, write) ) {
364 if( !ldif.Map(ldifname, read, write) ) {
365 cerr << "Read/Write name unknown: " << *i << endl;
368 else {
369 cerr << "Invalid map format: " << *i << endl;
375 int main(int argc, char *argv[])
377 cout.sync_with_stdio(true); // leave this on, since libusb uses
378 // stdio for debug messages
380 try {
382 uint32_t pin = 0;
383 bool list_only = false,
384 show_dbdb = false,
385 ldif_contacts = false,
386 data_dump = false,
387 reset_device = false,
388 list_contact_fields = false,
389 list_ldif_map = false,
390 record_state = false;
391 string ldifBaseDN, ldifDnAttr;
392 string filename;
393 string password;
394 vector<string> dbNames, saveDbNames, mapCommands;
395 vector<StateTableCommand> stCommands;
397 // process command line options
398 for(;;) {
399 int cmd = getopt(argc, argv, "c:C:d:D:f:hlLm:Mp:P:r:R:Ss:tT:vX");
400 if( cmd == -1 )
401 break;
403 switch( cmd )
405 case 'c': // contacts to ldap ldif
406 ldif_contacts = true;
407 ldifBaseDN = optarg;
408 break;
410 case 'C': // DN Attribute for FQDN
411 ldifDnAttr = optarg;
412 break;
414 case 'd': // show dbname
415 dbNames.push_back(string(optarg));
416 break;
418 case 'D': // delete record
419 stCommands.push_back(
420 StateTableCommand('D', false, atoi(optarg)));
421 break;
423 case 'f': // filename
424 #ifdef __BARRY_BOOST_MODE__
425 filename = optarg;
426 #else
427 cerr << "-f option not supported - no Boost "
428 "serialization support available\n";
429 return 1;
430 #endif
431 break;
432 case 'l': // list only
433 list_only = true;
434 break;
436 case 'L': // List Contact field names
437 list_contact_fields = true;
438 break;
440 case 'm': // Map / Unmap
441 mapCommands.push_back(string(optarg));
442 break;
444 case 'M': // List LDIF map
445 list_ldif_map = true;
446 break;
448 case 'p': // Blackberry PIN
449 pin = strtoul(optarg, NULL, 16);
450 break;
452 case 'P': // Device password
453 password = optarg;
454 break;
456 case 'r': // get specific record index
457 stCommands.push_back(
458 StateTableCommand('r', false, atoi(optarg)));
459 break;
461 case 'R': // same as 'r', and clears dirty
462 stCommands.push_back(
463 StateTableCommand('r', true, atoi(optarg)));
464 break;
466 case 's': // save dbname
467 saveDbNames.push_back(string(optarg));
468 break;
470 case 'S': // show supported databases
471 ShowParsers();
472 return 0;
474 case 't': // display database database
475 show_dbdb = true;
476 break;
478 case 'T': // show RecordStateTable
479 record_state = true;
480 dbNames.push_back(string(optarg));
481 break;
483 case 'v': // data dump on
484 data_dump = true;
485 break;
487 case 'X': // reset device
488 reset_device = true;
489 break;
491 case 'h': // help
492 default:
493 Usage();
494 return 0;
498 // Initialize the barry library. Must be called before
499 // anything else.
500 Barry::Init(data_dump);
502 // LDIF class... only needed if ldif output turned on
503 ContactLdif ldif(ldifBaseDN);
504 DoMapping(ldif, mapCommands);
505 if( ldifDnAttr.size() ) {
506 if( !ldif.SetDNAttr(ldifDnAttr) ) {
507 cerr << "Unable to set DN Attr: " << ldifDnAttr << endl;
511 // Probe the USB bus for Blackberry devices and display.
512 // If user has specified a PIN, search for it in the
513 // available device list here as well
514 Barry::Probe probe;
515 int activeDevice = -1;
516 if( ldif_contacts )
517 cout << "# ";
518 cout << "Blackberry devices found:" << endl;
519 for( int i = 0; i < probe.GetCount(); i++ ) {
520 if( ldif_contacts )
521 cout << "# ";
522 cout << probe.Get(i) << endl;
523 if( probe.Get(i).m_pin == pin )
524 activeDevice = i;
527 if( list_only )
528 return 0; // done
530 if( activeDevice == -1 ) {
531 if( pin == 0 ) {
532 // can we default to single device?
533 if( probe.GetCount() == 1 )
534 activeDevice = 0;
535 else {
536 cerr << "No device selected" << endl;
537 return 1;
540 else {
541 cerr << "PIN " << setbase(16) << pin
542 << " not found" << endl;
543 return 1;
547 if( reset_device ) {
548 Usb::Device dev(probe.Get(activeDevice).m_dev);
549 dev.Reset();
550 return 0;
553 // Create our controller object
554 Barry::Controller con(probe.Get(activeDevice));
557 // execute each mode that was turned on
561 // Dump list of all databases to stdout
562 if( show_dbdb ) {
563 // open desktop mode socket
564 con.OpenMode(Controller::Desktop, password.c_str());
565 cout << con.GetDBDB() << endl;
568 // Dump list of Contact field names
569 if( list_contact_fields ) {
570 for( const ContactLdif::NameToFunc *n = ldif.GetFieldNames(); n->name; n++ ) {
571 cout.fill(' ');
572 cout << " " << left << setw(20) << n->name << ": "
573 << n->description << endl;
577 // Dump current LDIF mapping
578 if( list_ldif_map ) {
579 cout << ldif << endl;
582 // Dump list of contacts to an LDAP LDIF file
583 // This uses the Controller convenience templates
584 if( ldif_contacts ) {
585 // make sure we're in desktop mode
586 con.OpenMode(Controller::Desktop, password.c_str());
588 // create a storage functor object that accepts
589 // Barry::Contact objects as input
590 Contact2Ldif storage(ldif);
592 // load all the Contact records into storage
593 con.LoadDatabaseByType<Barry::Contact>(storage);
596 // Dump record state table to stdout
597 if( record_state ) {
598 if( dbNames.size() == 0 ) {
599 cout << "No db names to process" << endl;
600 return 1;
603 vector<string>::iterator b = dbNames.begin();
604 for( ; b != dbNames.end(); b++ ) {
605 con.OpenMode(Controller::Desktop, password.c_str());
606 unsigned int id = con.GetDBID(*b);
607 RecordStateTable state;
608 con.GetRecordStateTable(id, state);
609 cout << "Record state table for: " << *b << endl;
610 cout << state;
612 return 0;
615 // Get Record mode overrides the default name mode
616 if( stCommands.size() ) {
617 if( dbNames.size() != 1 ) {
618 cout << "Must have 1 db name to process" << endl;
619 return 1;
622 con.OpenMode(Controller::Desktop, password.c_str());
623 unsigned int id = con.GetDBID(dbNames[0]);
624 auto_ptr<Parser> parse = GetParser(dbNames[0],filename);
626 for( unsigned int i = 0; i < stCommands.size(); i++ ) {
627 con.GetRecord(id, stCommands[i].index, *parse.get());
629 if( stCommands[i].flag == 'r' && stCommands[i].clear ) {
630 cout << "Clearing record's dirty flags..." << endl;
631 con.ClearDirty(id, stCommands[i].index);
634 if( stCommands[i].flag == 'D' ) {
635 con.DeleteRecord(id, stCommands[i].index);
639 return 0;
642 // Dump contents of selected databases to stdout, or
643 // to file if specified.
644 // This is retrieving data from the Blackberry.
645 if( dbNames.size() ) {
646 vector<string>::iterator b = dbNames.begin();
648 for( ; b != dbNames.end(); b++ ) {
649 con.OpenMode(Controller::Desktop, password.c_str());
650 auto_ptr<Parser> parse = GetParser(*b,filename);
651 unsigned int id = con.GetDBID(*b);
652 con.LoadDatabase(id, *parse.get());
656 // Save contents of file to specified databases
657 // This is writing data to the Blackberry.
658 if( saveDbNames.size() ) {
659 vector<string>::iterator b = saveDbNames.begin();
661 for( ; b != saveDbNames.end(); b++ ) {
662 con.OpenMode(Controller::Desktop, password.c_str());
663 auto_ptr<Builder> build =
664 GetBuilder(*b, filename);
665 unsigned int id = con.GetDBID(*b);
666 con.SaveDatabase(id, *build);
671 catch( Usb::Error &ue) {
672 std::cerr << "Usb::Error caught: " << ue.what() << endl;
673 return 1;
675 catch( Barry::Error &se ) {
676 std::cerr << "Barry::Error caught: " << se.what() << endl;
677 return 1;
679 catch( std::exception &e ) {
680 std::cerr << "std::exception caught: " << e.what() << endl;
681 return 1;
684 return 0;