3 /// Barry Input / Output
7 Copyright (C) 2010, 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 <barry/barrysync.h>
24 #include <barry/barrybackup.h>
42 using namespace std::tr1
;
43 using namespace Barry
;
48 const char *Version
= Barry::Version(major
, minor
);
51 << "bio - Barry Input / Output\n"
52 << " Copyright 2010, Net Direct Inc. (http://www.netdirect.ca/)\n"
53 << " Using: " << Version
<< "\n"
55 #ifdef __BARRY_BOOST_MODE__
62 << " Usage: bio -i <type> [options...] -o <type> [options...]\n"
64 << " -i type The input type (Builder) to use for producing records\n"
65 << " Can be one of: device, tar"
66 #ifdef __BARRY_BOOST_MODE__
70 << " -o type The output type (Parser) to use for processing records.\n"
71 << " Multiple outputs are allowed, as long as they don't\n"
72 << " conflict (such as two outputs writing to the same file\n"
74 << " Can be one of: device, tar"
75 #ifdef __BARRY_BOOST_MODE__
78 << ", ldif, mime, dump, sha1, cstore\n"
80 << " Options to use for 'device' type:\n"
81 << " -d db Name of input database. Can be used multiple times.\n"
82 << " -A Add all available device databases, instead of specifying\n"
83 << " them manually via -d\n"
84 << " -p pin PIN of device to talk to\n"
85 << " If only one device is plugged in, this flag is optional\n"
86 << " -P pass Simplistic method to specify device password\n"
87 << " -w mode Set write mode when using 'device' for output. Must be\n"
88 << " specified, or will not write anything.\n"
89 << " Can be one of: erase, overwrite, addonly, addnew\n"
91 // FIXME - modifiers not yet implemented
93 << " Input database modifiers: (can be used multiple times for more than 1 record)\n"
95 << " -r # Record index number as seen in the -T state table.\n"
96 << " This overrides the default -d behaviour, and only\n"
97 << " downloads the one specified record, sending to stdout.\n"
98 << " -R # Same as -r, but also clears the record's dirty flags.\n"
99 << " -D # Record index number as seen in the -T state table,\n"
100 << " which indicates the record to delete. Used with the -d\n"
101 << " command to specify the database.\n"
104 << " Options to use for 'tar' backup type:\n"
105 << " -d db Name of input database. Can be used multiple times.\n"
106 << " Not available in output mode. Note that by default,\n"
107 << " all databases in the backup are selected, when reading,\n"
108 << " unless at least one -d is specified.\n"
109 << " -f file Tar backup file to read from or write to\n"
110 #ifdef __BARRY_BOOST_MODE__
112 << " Options to use for 'boost' type:\n"
113 << " -f file Boost serialization filename to read from or write to\n"
114 << " Can use - to specify stdin/stdout\n"
117 << " Options to use for 'ldif' type:\n"
118 << " -c dn Convert address book database to LDIF format, using the\n"
119 << " specified baseDN\n"
120 << " -C dnattr LDIF attribute name to use when building the FQDN\n"
121 << " Defaults to 'cn'\n"
125 << " -L List Contact field names\n"
126 << " -m Map LDIF name to Contact field / Unmap LDIF name\n"
127 << " Map: ldif,read,write - maps ldif to read/write Contact fields\n"
128 << " Unmap: ldif name alone\n"
129 << " -M List current LDIF mapping\n"
132 << " Options to use for 'mime' type:\n"
133 << " -f file Filename to read from or write to. Use - to explicitly\n"
134 << " specify stdin/stdout, which is default.\n"
136 << " Options to use for 'dump' to stdout output type:\n"
137 << " -n Use hex dump parser on all databases.\n"
139 << " Options to use for 'sha1' sum stdout output type:\n"
140 << " -t Include DB Name, Type, and Unique record IDs in the checksums\n"
142 << " Options to use for 'cstore' output type:\n"
143 << " -l List filenames only\n"
144 << " -f file Filename from the above list, including path.\n"
145 << " If found, the file will be written to the current\n"
146 << " directory, using the base filename from the device.\n"
148 << " Standalone options:\n"
150 << " -I cs International charset for string conversions\n"
151 << " Valid values here are available with 'iconv --list'\n"
152 << " -S Show list of supported database parsers and builders\n"
153 << " -v Dump protocol data during operation\n"
161 virtual ~ModeBase() {}
163 virtual bool ProbeNeeded() const { return false; }
165 virtual void SetFilename(const std::string
&name
)
167 throw runtime_error("Filename not applicable for this mode");
170 virtual void AddDB(const std::string
&dbname
)
172 throw runtime_error("DB not applicable for this mode");
175 virtual void AddAllDBs()
177 throw runtime_error("DBs not applicable for this mode");
180 virtual void SetPIN(const std::string
&pin
)
182 throw runtime_error("PIN not applicable for this mode");
185 virtual void SetPassword(const std::string
&password
)
187 throw runtime_error("Password not applicable for this mode");
190 virtual void SetWriteMode(DeviceParser::WriteMode mode
)
192 throw runtime_error("Device write behaviour not applicable for this mode");
195 virtual void SetDN(const std::string
&dn
)
197 throw runtime_error("DN not applicable for this mode");
200 virtual void SetAttribute(const std::string
&attr
)
202 throw runtime_error("Attribute not applicable for this mode");
205 virtual void SetHexDump()
207 throw runtime_error("No hex dump option in this mode");
210 virtual void IncludeIDs()
212 throw runtime_error("Including record IDs in the SHA1 sum is not applicable in this mode");
215 virtual void SetList()
217 throw runtime_error("List option not applicable for this mode");
221 class DeviceBase
: public virtual ModeBase
225 std::string m_password
;
228 bool ProbeNeeded() const { return true; }
230 void SetPIN(const std::string
&pin
)
232 istringstream
iss(pin
);
235 throw runtime_error("Invalid PIN: " + pin
);
238 void SetPassword(const std::string
&password
)
240 m_password
= password
;
244 //////////////////////////////////////////////////////////////////////////////
245 // Base class for Input Mode
247 class InputBase
: public virtual ModeBase
250 virtual Builder
& GetBuilder(Barry::Probe
*probe
, IConverter
&ic
) = 0;
253 class DeviceInputBase
: public DeviceBase
, public InputBase
257 //////////////////////////////////////////////////////////////////////////////
258 // Mode: Input, Type: device
260 class DeviceInput
: public DeviceInputBase
262 auto_ptr
<Controller
> m_con
;
263 auto_ptr
<Mode::Desktop
> m_desktop
;
264 auto_ptr
<DeviceBuilder
> m_builder
;
265 vector
<string
> m_dbnames
;
274 void AddDB(const std::string
&dbname
)
276 m_dbnames
.push_back(dbname
);
284 Builder
& GetBuilder(Barry::Probe
*probe
, IConverter
&ic
)
286 int i
= probe
->FindActive(m_pin
);
289 throw runtime_error("PIN not found: " + m_pin
.Str());
291 throw runtime_error("PIN not specified, and more than one device exists.");
294 m_con
.reset( new Controller(probe
->Get(i
)) );
295 m_desktop
.reset( new Mode::Desktop(*m_con
, ic
) );
296 m_desktop
->Open(m_password
.c_str());
297 m_builder
.reset( new DeviceBuilder(*m_desktop
) );
300 m_builder
->Add(m_desktop
->GetDBDB());
303 for( size_t i
= 0; i
< m_dbnames
.size(); i
++ ) {
304 m_builder
->Add(m_dbnames
[i
]);
312 //////////////////////////////////////////////////////////////////////////////
313 // Mode: Input, Type: tar
315 class TarInput
: public InputBase
317 auto_ptr
<Restore
> m_restore
;
319 vector
<string
> m_dbnames
;
322 void SetFilename(const std::string
&name
)
326 throw runtime_error("Cannot use stdin as tar source file, sorry.");
329 void AddDB(const std::string
&dbname
)
331 m_dbnames
.push_back(dbname
);
334 Builder
& GetBuilder(Barry::Probe
*probe
, IConverter
&ic
)
336 m_restore
.reset( new Restore(m_tarpath
, true) );
337 for( size_t i
= 0; i
< m_dbnames
.size(); i
++ ) {
338 m_restore
->AddDB(m_dbnames
[i
]);
345 //////////////////////////////////////////////////////////////////////////////
346 // Mode: Input, Type: boost
348 #ifdef __BARRY_BOOST_MODE__
349 class BoostInput
: public InputBase
351 auto_ptr
<BoostBuilder
> m_builder
;
356 : m_filename("-") // default to stdin/stdout
360 void SetFilename(const std::string
&name
)
365 Builder
& GetBuilder(Barry::Probe
*probe
, IConverter
&ic
)
367 if( m_filename
== "-" ) {
369 m_builder
.reset( new BoostBuilder(cin
) );
372 m_builder
.reset( new BoostBuilder(m_filename
) );
380 //////////////////////////////////////////////////////////////////////////////
381 // Mode: Input, Type: ldif
383 class LdifInput
: public InputBase
385 auto_ptr
<Builder
> m_builder
;
394 void SetFilename(const std::string
&name
)
399 Builder
& GetBuilder(Barry::Probe
*probe
, IConverter
&ic
)
401 if( m_filename
== "-" ) {
404 new RecordBuilder
<Contact
, LdifStore
>(
405 new LdifStore(cin
)) );
409 new RecordBuilder
<Contact
, LdifStore
>(
410 new LdifStore(m_filename
)) );
418 //////////////////////////////////////////////////////////////////////////////
419 // Mode: Input, Type: mime
421 class MimeInput
: public InputBase
423 auto_ptr
<MimeBuilder
> m_builder
;
432 void SetFilename(const std::string
&name
)
437 Builder
& GetBuilder(Barry::Probe
*probe
, IConverter
&ic
)
439 if( m_filename
== "-" ) {
441 m_builder
.reset( new MimeBuilder(cin
) );
444 m_builder
.reset( new MimeBuilder(m_filename
) );
451 //////////////////////////////////////////////////////////////////////////////
452 // Base class for Output Mode
454 class OutputBase
: public virtual ModeBase
457 virtual Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
) = 0;
460 class DeviceOutputBase
: public DeviceBase
, public OutputBase
464 //////////////////////////////////////////////////////////////////////////////
465 // Mode: Output, Type: device
467 class DeviceOutput
: public DeviceOutputBase
469 auto_ptr
<Controller
> m_con
;
470 auto_ptr
<Mode::Desktop
> m_desktop
;
471 auto_ptr
<DeviceParser
> m_parser
;
472 DeviceParser::WriteMode m_mode
;
476 : m_mode(DeviceParser::DROP_RECORD
)
480 void SetWriteMode(DeviceParser::WriteMode mode
)
485 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
487 int i
= probe
->FindActive(m_pin
);
490 throw runtime_error("PIN not found: " + m_pin
.Str());
492 throw runtime_error("PIN not specified, and more than one device exists.");
495 m_con
.reset( new Controller(probe
->Get(i
)) );
496 m_desktop
.reset( new Mode::Desktop(*m_con
, ic
) );
497 m_desktop
->Open(m_password
.c_str());
498 m_parser
.reset( new DeviceParser(*m_desktop
, m_mode
) );
504 //////////////////////////////////////////////////////////////////////////////
505 // Mode: Output, Type: tar
507 class TarOutput
: public OutputBase
509 auto_ptr
<Backup
> m_backup
;
513 void SetFilename(const std::string
&name
)
517 throw runtime_error("Cannot use stdout as tar backup file, sorry.");
520 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
522 m_backup
.reset( new Backup(m_tarpath
) );
527 //////////////////////////////////////////////////////////////////////////////
528 // Mode: Output, Type: boost
530 #ifdef __BARRY_BOOST_MODE__
531 class BoostOutput
: public OutputBase
533 auto_ptr
<BoostParser
> m_parser
;
537 void SetFilename(const std::string
&name
)
542 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
544 if( !m_filename
.size() )
545 throw runtime_error("Boost output requires a specific output file (-f switch)");
547 if( m_filename
== "-" ) {
549 m_parser
.reset( new BoostParser(cout
) );
552 m_parser
.reset( new BoostParser(m_filename
) );
560 //////////////////////////////////////////////////////////////////////////////
561 // Mode: Output, Type: ldif
563 class LdifOutput
: public OutputBase
565 auto_ptr
<Parser
> m_parser
;
576 void SetFilename(const std::string
&name
)
581 void SetDN(const std::string
&dn
)
586 void SetAttribute(const std::string
&attr
)
591 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
593 if( m_filename
== "-" ) {
596 new RecordParser
<Contact
, LdifStore
>(
597 new LdifStore(cout
, m_baseDN
,
602 new RecordParser
<Contact
, LdifStore
>(
603 new LdifStore(m_filename
, m_baseDN
,
610 //////////////////////////////////////////////////////////////////////////////
611 // Mode: Output, Type: mime
613 class MimeStore
: public AllRecordStore
618 MimeStore(std::ostream
&os
)
624 #define HANDLE_PARSER(tname) \
625 void operator() (const Barry::tname &r) \
627 MimeDump<tname>::Dump(m_os, r); \
630 ALL_KNOWN_PARSER_TYPES
633 class MimeOutput
: public OutputBase
635 auto_ptr
<std::ofstream
> m_file
;
636 auto_ptr
<Parser
> m_parser
;
637 std::string m_filename
;
641 : m_filename("-") // default to stdout
645 void SetFilename(const std::string
&name
)
650 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
652 if( m_filename
== "-" ) {
653 m_parser
.reset( new AllRecordParser(cout
,
654 new HexDumpParser(cout
),
655 new MimeStore(cout
)) );
658 m_file
.reset( new std::ofstream(m_filename
.c_str()) );
659 m_parser
.reset( new AllRecordParser(*m_file
,
660 new HexDumpParser(*m_file
),
661 new MimeStore(*m_file
)) );
667 //////////////////////////////////////////////////////////////////////////////
668 // Mode: Output, Type: dump
670 class DumpOutput
: public OutputBase
672 auto_ptr
<Parser
> m_parser
;
686 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
689 m_parser
.reset( new HexDumpParser(cout
) );
692 m_parser
.reset( new AllRecordParser(cout
,
693 new HexDumpParser(cout
),
694 new AllRecordDumpStore(cout
)) );
700 //////////////////////////////////////////////////////////////////////////////
701 // Mode: Output, Type: sha1
703 class Sha1Output
: public OutputBase
705 auto_ptr
<Parser
> m_parser
;
710 : m_include_ids(false)
716 m_include_ids
= true;
719 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
721 m_parser
.reset( new ChecksumParser(m_include_ids
) );
726 //////////////////////////////////////////////////////////////////////////////
727 // Mode: Output, Type: cstore
729 class ContentStoreOutput
: public OutputBase
731 auto_ptr
<Parser
> m_parser
;
733 vector
<string
> m_filenames
;
741 void SetFilename(const std::string
&name
)
743 m_filenames
.push_back(name
);
751 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
753 m_parser
.reset( new RecordParser
<ContentStore
, ContentStoreOutput
>(*this) );
758 void operator() (const ContentStore
&rec
)
761 cout
<< rec
.Filename
;
762 if( rec
.FolderFlag
) {
768 // check if this record matches one of the filenames
770 vector
<string
>::iterator i
= find(m_filenames
.begin(),
771 m_filenames
.end(), rec
.Filename
);
772 if( i
!= m_filenames
.end() ) {
778 void SaveFile(const ContentStore
&rec
)
780 size_t slash
= rec
.Filename
.rfind('/');
782 if( slash
== string::npos
)
783 filename
= rec
.Filename
;
785 filename
= rec
.Filename
.substr(slash
+ 1);
787 // modify filename until we find one that doesn't
789 string freshname
= filename
;
791 while( access(freshname
.c_str(), F_OK
) == 0 ) {
793 oss
<< filename
<< count
++;
794 freshname
= oss
.str();
798 cout
<< "Saving: " << rec
.Filename
799 << " as " << freshname
<< endl
;
800 ofstream
ofs(freshname
.c_str());
801 ofs
<< rec
.FileContent
;
804 cout
<< "Error during write!" << endl
;
811 //////////////////////////////////////////////////////////////////////////////
812 // Main application class
817 typedef shared_ptr
<OutputBase
> OutputPtr
;
818 typedef vector
<OutputPtr
> OutputsType
;
821 auto_ptr
<InputBase
> Input
;
826 bool ParseInMode(const string
&mode
);
827 bool ParseOutMode(const string
&mode
);
828 DeviceParser::WriteMode
ParseWriteMode(const std::string
&mode
);
829 static void ShowParsers();
830 // returns true if any of the items in Outputs needs a probe
831 bool OutputsProbeNeeded();
832 int main(int argc
, char *argv
[]);
835 bool App::ParseInMode(const string
&mode
)
837 if( mode
== "device" ) {
838 Input
.reset( new DeviceInput
);
841 else if( mode
== "tar" ) {
842 Input
.reset( new TarInput
);
845 #ifdef __BARRY_BOOST_MODE__
846 else if( mode
== "boost" ) {
847 Input
.reset( new BoostInput
);
851 else if( mode
== "ldif" ) {
852 Input
.reset( new LdifInput
);
855 else if( mode
== "mime" ) {
856 Input
.reset( new MimeInput
);
863 bool App::ParseOutMode(const string
&mode
)
865 if( mode
== "device" ) {
866 Outputs
.push_back( OutputPtr(new DeviceOutput
) );
869 else if( mode
== "tar" ) {
870 Outputs
.push_back( OutputPtr(new TarOutput
) );
873 #ifdef __BARRY_BOOST_MODE__
874 else if( mode
== "boost" ) {
875 Outputs
.push_back( OutputPtr(new BoostOutput
) );
879 else if( mode
== "ldif" ) {
880 Outputs
.push_back( OutputPtr(new LdifOutput
) );
883 else if( mode
== "mime" ) {
884 Outputs
.push_back( OutputPtr(new MimeOutput
) );
887 else if( mode
== "dump" ) {
888 Outputs
.push_back( OutputPtr(new DumpOutput
) );
891 else if( mode
== "sha1" ) {
892 Outputs
.push_back( OutputPtr(new Sha1Output
) );
895 else if( mode
== "cstore" ) {
896 Outputs
.push_back( OutputPtr(new ContentStoreOutput
) );
903 DeviceParser::WriteMode
App::ParseWriteMode(const std::string
&mode
)
905 if( mode
== "erase" )
906 return DeviceParser::ERASE_ALL_WRITE_ALL
;
907 else if( mode
== "overwrite" )
908 return DeviceParser::INDIVIDUAL_OVERWRITE
;
909 else if( mode
== "addonly" )
910 return DeviceParser::ADD_BUT_NO_OVERWRITE
;
911 else if( mode
== "addnew" )
912 return DeviceParser::ADD_WITH_NEW_ID
;
914 throw runtime_error("Unknown device output mode. Must be one of: erase, overwrite, addonly, addnew");
917 void App::ShowParsers()
919 cout
<< "Supported Database parsers:\n"
920 << " (* = can display in vformat MIME mode)\n"
923 #define HANDLE_PARSER(tname) \
924 << " " << tname::GetDBName() \
925 << (MimeDump<tname>::Supported() ? " *" : "") << "\n"
927 ALL_KNOWN_PARSER_TYPES
930 << "Supported Database builders:\n"
932 #undef HANDLE_BUILDER
933 #define HANDLE_BUILDER(tname) \
934 << " " << tname::GetDBName() << "\n"
936 ALL_KNOWN_BUILDER_TYPES
941 bool App::OutputsProbeNeeded()
943 for( OutputsType::iterator i
= Outputs
.begin();
947 if( (*i
)->ProbeNeeded() )
953 int App::main(int argc
, char *argv
[])
955 bool verbose
= false;
958 // process command line options
959 ModeBase
*current
= 0;
961 int cmd
= getopt(argc
, argv
, "hi:o:nvI:f:p:P:d:c:C:ASw:tl");
965 // first option must be in or out, or a global option
980 case 'i': // set input mode
981 // must be first time used
982 if( Input
.get() || !ParseInMode(optarg
) ) {
986 current
= Input
.get();
989 case 'o': // set output mode
990 // can be used multiple times
991 if( !ParseOutMode(optarg
) ) {
995 current
= Outputs
[Outputs
.size() - 1].get();
999 case 'c': // set ldif dn
1000 current
->SetDN(optarg
);
1003 case 'C': // set ldif attr
1004 current
->SetAttribute(optarg
);
1007 case 'd': // database name
1008 current
->AddDB(optarg
);
1011 case 'f': // filename
1012 current
->SetFilename(optarg
);
1015 case 'p': // device PIN
1016 current
->SetPIN(optarg
);
1019 case 'P': // password
1020 current
->SetPassword(optarg
);
1023 case 'w': // device write mode
1024 current
->SetWriteMode(ParseWriteMode(optarg
));
1027 case 'A': // add all DB names to the device builder
1028 current
->AddAllDBs();
1031 case 't': // include type and IDs in sha1 mode
1032 current
->IncludeIDs();
1035 case 'l': // list only
1039 case 'S': // show parsers and builders
1043 case 'I': // international charset (iconv)
1044 iconvCharset
= optarg
;
1047 case 'n': // use null hex dump parser only
1048 current
->SetHexDump();
1051 case 'v': // verbose
1062 if( !Input
.get() || !Outputs
.size() ) {
1067 // Initialize the Barry library
1068 Barry::Init(verbose
);
1070 // Create an IConverter object if needed
1071 auto_ptr
<IConverter
> ic
;
1072 if( iconvCharset
.size() ) {
1073 ic
.reset( new IConverter(iconvCharset
.c_str(), true) );
1076 // Probe for devices only if needed
1077 auto_ptr
<Probe
> probe
;
1078 if( Input
->ProbeNeeded() || OutputsProbeNeeded() ) {
1079 // Probe for available devices
1080 probe
.reset( new Probe
);
1083 // Setup the input first (builder)
1084 Builder
&builder
= Input
->GetBuilder(probe
.get(), *ic
);
1086 // Setup a TeeParser with all Outputs
1088 for( OutputsType::iterator i
= Outputs
.begin(); i
!= Outputs
.end(); ++i
) {
1089 Parser
&parser
= (*i
)->GetParser(probe
.get(), *ic
);
1095 pipe
.PumpFile(tee
, ic
.get());
1100 int main(int argc
, char *argv
[])
1104 return app
.main(argc
, argv
);
1106 catch( std::exception
&e
) {
1107 cerr
<< "Exception: " << e
.what() << endl
;