3 /// Barry Input / Output
7 Copyright (C) 2010-2012, 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>
41 #include "barrygetopt.h"
44 using namespace std::tr1
;
45 using namespace Barry
;
47 // keeping a record of all the -i device / -o device pin numbers, so
48 // we can warn the poor user appropriately
49 std::vector
<Barry::Pin
> m_device_pins
;
51 bool IsPinUsed(const Barry::Pin
&pin
)
53 for( std::vector
<Barry::Pin
>::const_iterator b
= m_device_pins
.begin();
54 b
!= m_device_pins
.end(); ++b
)
64 int logical
, major
, minor
;
65 const char *Version
= Barry::Version(logical
, major
, minor
);
68 << "bio - Barry Input / Output\n"
69 << " Copyright 2010-2012, Net Direct Inc. (http://www.netdirect.ca/)\n"
70 << " Using: " << Version
<< "\n"
72 #ifdef __BARRY_BOOST_MODE__
79 << " Usage: bio -i <type> [options...] -o <type> [options...]\n"
81 << " -i type The input type (Builder) to use for producing records\n"
82 << " Can be one of: device, tar"
83 #ifdef __BARRY_BOOST_MODE__
87 << " -o type The output type (Parser) to use for processing records.\n"
88 << " Multiple outputs are allowed, as long as they don't\n"
89 << " conflict (such as two outputs writing to the same file\n"
91 << " Can be one of: device, tar"
92 #ifdef __BARRY_BOOST_MODE__
95 << ", ldif, mime, dump, sha1, cstore\n"
97 << " Options to use for 'device' type:\n"
98 << " -d db Name of input database. Can be used multiple times.\n"
99 << " -A Add all available device databases, instead of specifying\n"
100 << " them manually via -d\n"
101 << " -p pin PIN of device to talk to\n"
102 << " If only one device is plugged in, this flag is optional\n"
103 << " -P pass Simplistic method to specify device password\n"
104 << " -w mode Set write mode when using 'device' for output. Must be\n"
105 << " specified, or will not write anything.\n"
106 << " Can be one of: erase, overwrite, addonly, addnew\n"
108 << " Options to use for 'tar' backup type:\n"
109 << " -d db Name of input database. Can be used multiple times.\n"
110 << " Not available in output mode. Note that by default,\n"
111 << " all databases in the backup are selected, when reading,\n"
112 << " unless at least one -d is specified.\n"
113 << " -D db Name of input database to skip. If no -d options are used,\n"
114 << " then all databases are automatically selected. Using -D\n"
115 << " allows a filtering selection. If -d and -D are used for\n"
116 << " the same database, -D takes precedence.\n"
117 << " -f file Tar backup file to read from or write to\n"
118 #ifdef __BARRY_BOOST_MODE__
120 << " Options to use for 'boost' type:\n"
121 << " -f file Boost serialization filename to read from or write to\n"
122 << " Can use - to specify stdin/stdout\n"
125 << " Options to use for 'ldif' type:\n"
126 << " -c dn Convert address book database to LDIF format, using the\n"
127 << " specified baseDN\n"
128 << " -C dnattr LDIF attribute name to use when building the FQDN\n"
129 << " Defaults to 'cn'\n"
133 << " -L List Contact field names\n"
134 << " -m Map LDIF name to Contact field / Unmap LDIF name\n"
135 << " Map: ldif,read,write - maps ldif to read/write Contact fields\n"
136 << " Unmap: ldif name alone\n"
137 << " -M List current LDIF mapping\n"
140 << " Options to use for 'mime' type:\n"
141 << " -f file Filename to read from or write to. Use - to explicitly\n"
142 << " specify stdin/stdout, which is default.\n"
144 << " Options to use for 'dump' to stdout output type:\n"
145 << " -n Use hex dump parser on all databases.\n"
147 << " Options to use for 'sha1' sum stdout output type:\n"
148 << " -t Include DB Name, Type, and Unique record IDs in the checksums\n"
150 << " Options to use for 'cstore' output type:\n"
151 << " -l List filenames only\n"
152 << " -f file Filename from the above list, including path.\n"
153 << " If found, the file will be written to the current\n"
154 << " directory, using the base filename from the device.\n"
156 << " Standalone options:\n"
158 << " -I cs International charset for string conversions\n"
159 << " Valid values here are available with 'iconv --list'\n"
160 << " -S Show list of supported database parsers and builders.\n"
161 << " Use twice to show field names as well.\n"
162 << " -v Dump protocol data during operation\n"
170 virtual ~ModeBase() {}
172 virtual bool ProbeNeeded() const { return false; }
174 virtual void SetFilename(const std::string
&name
)
176 throw runtime_error("Filename not applicable for this mode");
179 virtual void AddDB(const std::string
&dbname
)
181 throw runtime_error("DB not applicable for this mode");
184 virtual void AddSkipDB(const std::string
&dbname
)
186 throw runtime_error("DB skipping not applicable for this mode");
189 virtual void AddAllDBs()
191 throw runtime_error("DBs not applicable for this mode");
194 virtual void SetPIN(const std::string
&pin
)
196 throw runtime_error("PIN not applicable for this mode");
199 virtual void SetPassword(const std::string
&password
)
201 throw runtime_error("Password not applicable for this mode");
204 virtual void SetWriteMode(DeviceParser::WriteMode mode
)
206 throw runtime_error("Device write behaviour not applicable for this mode");
209 virtual void SetDN(const std::string
&dn
)
211 throw runtime_error("DN not applicable for this mode");
214 virtual void SetAttribute(const std::string
&attr
)
216 throw runtime_error("Attribute not applicable for this mode");
219 virtual void SetHexDump()
221 throw runtime_error("No hex dump option in this mode");
224 virtual void IncludeIDs()
226 throw runtime_error("Including record IDs in the SHA1 sum is not applicable in this mode");
229 virtual void SetList()
231 throw runtime_error("List option not applicable for this mode");
235 class DeviceBase
: public virtual ModeBase
239 std::string m_password
;
242 bool ProbeNeeded() const { return true; }
244 void SetPIN(const std::string
&pin
)
246 istringstream
iss(pin
);
249 throw runtime_error("Invalid PIN: " + pin
);
252 void SetPassword(const std::string
&password
)
254 m_password
= password
;
258 //////////////////////////////////////////////////////////////////////////////
259 // Base class for Input Mode
261 class InputBase
: public virtual ModeBase
264 virtual Builder
& GetBuilder(Barry::Probe
*probe
, IConverter
&ic
) = 0;
267 class DeviceInputBase
: public DeviceBase
, public InputBase
271 //////////////////////////////////////////////////////////////////////////////
272 // Mode: Input, Type: device
274 class DeviceInput
: public DeviceInputBase
276 auto_ptr
<Controller
> m_con
;
277 auto_ptr
<Mode::Desktop
> m_desktop
;
278 auto_ptr
<DeviceBuilder
> m_builder
;
279 vector
<string
> m_dbnames
;
288 void AddDB(const std::string
&dbname
)
290 m_dbnames
.push_back(dbname
);
298 Builder
& GetBuilder(Barry::Probe
*probe
, IConverter
&ic
)
300 int i
= probe
->FindActive(m_pin
);
303 throw runtime_error("PIN not found: " + m_pin
.Str());
305 throw runtime_error("PIN not specified, and more than one device exists.");
308 if( IsPinUsed(probe
->Get(i
).m_pin
) ) {
309 throw runtime_error("It seems you are trying to use the same device for multiple input or outputs.");
311 m_device_pins
.push_back(probe
->Get(i
).m_pin
);
313 m_con
.reset( new Controller(probe
->Get(i
)) );
314 m_desktop
.reset( new Mode::Desktop(*m_con
, ic
) );
315 m_desktop
->Open(m_password
.c_str());
316 m_builder
.reset( new DeviceBuilder(*m_desktop
) );
319 m_builder
->Add(m_desktop
->GetDBDB());
322 for( size_t i
= 0; i
< m_dbnames
.size(); i
++ ) {
323 if( !m_builder
->Add(m_dbnames
[i
]) )
324 throw runtime_error("Database not found: " + m_dbnames
[i
]);
332 //////////////////////////////////////////////////////////////////////////////
333 // Mode: Input, Type: tar
335 class TarInput
: public InputBase
337 auto_ptr
<Restore
> m_restore
;
339 vector
<string
> m_dbnames
, m_dbskips
;
342 void SetFilename(const std::string
&name
)
346 throw runtime_error("Cannot use stdin as tar source file, sorry.");
349 void AddDB(const std::string
&dbname
)
351 m_dbnames
.push_back(dbname
);
354 void AddSkipDB(const std::string
&dbname
)
356 m_dbskips
.push_back(dbname
);
359 Builder
& GetBuilder(Barry::Probe
*probe
, IConverter
&ic
)
361 m_restore
.reset( new Restore(m_tarpath
, true) );
362 for( size_t i
= 0; i
< m_dbnames
.size(); i
++ ) {
363 m_restore
->AddDB(m_dbnames
[i
]);
365 for( size_t i
= 0; i
< m_dbskips
.size(); i
++ ) {
366 m_restore
->AddSkipDB(m_dbskips
[i
]);
373 //////////////////////////////////////////////////////////////////////////////
374 // Mode: Input, Type: boost
376 #ifdef __BARRY_BOOST_MODE__
377 class BoostInput
: public InputBase
379 auto_ptr
<BoostBuilder
> m_builder
;
384 : m_filename("-") // default to stdin/stdout
388 void SetFilename(const std::string
&name
)
393 Builder
& GetBuilder(Barry::Probe
*probe
, IConverter
&ic
)
395 if( m_filename
== "-" ) {
397 m_builder
.reset( new BoostBuilder(cin
) );
400 m_builder
.reset( new BoostBuilder(m_filename
) );
408 //////////////////////////////////////////////////////////////////////////////
409 // Mode: Input, Type: ldif
411 class LdifInput
: public InputBase
413 auto_ptr
<Builder
> m_builder
;
422 void SetFilename(const std::string
&name
)
427 Builder
& GetBuilder(Barry::Probe
*probe
, IConverter
&ic
)
429 if( m_filename
== "-" ) {
432 new RecordBuilder
<Contact
, LdifStore
>(
433 new LdifStore(cin
)) );
437 new RecordBuilder
<Contact
, LdifStore
>(
438 new LdifStore(m_filename
)) );
446 //////////////////////////////////////////////////////////////////////////////
447 // Mode: Input, Type: mime
449 class MimeInput
: public InputBase
451 auto_ptr
<MimeBuilder
> m_builder
;
460 void SetFilename(const std::string
&name
)
465 Builder
& GetBuilder(Barry::Probe
*probe
, IConverter
&ic
)
467 if( m_filename
== "-" ) {
469 m_builder
.reset( new MimeBuilder(cin
) );
472 m_builder
.reset( new MimeBuilder(m_filename
) );
479 //////////////////////////////////////////////////////////////////////////////
480 // Base class for Output Mode
482 class OutputBase
: public virtual ModeBase
485 virtual Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
) = 0;
488 class DeviceOutputBase
: public DeviceBase
, public OutputBase
492 //////////////////////////////////////////////////////////////////////////////
493 // Mode: Output, Type: device
495 class DeviceOutput
: public DeviceOutputBase
497 auto_ptr
<Controller
> m_con
;
498 auto_ptr
<Mode::Desktop
> m_desktop
;
499 auto_ptr
<DeviceParser
> m_parser
;
500 DeviceParser::WriteMode m_mode
;
504 : m_mode(DeviceParser::DROP_RECORD
)
508 void SetWriteMode(DeviceParser::WriteMode mode
)
513 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
515 if( m_mode
== DeviceParser::DROP_RECORD
) {
516 cerr
<< "Warning: the -w switch was not specified: no data will be written to the device." << endl
;
519 int i
= probe
->FindActive(m_pin
);
522 throw runtime_error("PIN not found: " + m_pin
.Str());
524 throw runtime_error("PIN not specified, and more than one device exists.");
527 if( IsPinUsed(probe
->Get(i
).m_pin
) ) {
528 throw runtime_error("It seems you are trying to use the same device for multiple input or outputs.");
530 m_device_pins
.push_back(probe
->Get(i
).m_pin
);
532 m_con
.reset( new Controller(probe
->Get(i
)) );
533 m_desktop
.reset( new Mode::Desktop(*m_con
, ic
) );
534 m_desktop
->Open(m_password
.c_str());
535 m_parser
.reset( new DeviceParser(*m_desktop
, m_mode
) );
541 //////////////////////////////////////////////////////////////////////////////
542 // Mode: Output, Type: tar
544 class TarOutput
: public OutputBase
546 auto_ptr
<Backup
> m_backup
;
550 void SetFilename(const std::string
&name
)
554 throw runtime_error("Cannot use stdout as tar backup file, sorry.");
557 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
559 m_backup
.reset( new Backup(m_tarpath
) );
564 //////////////////////////////////////////////////////////////////////////////
565 // Mode: Output, Type: boost
567 #ifdef __BARRY_BOOST_MODE__
568 class BoostOutput
: public OutputBase
570 auto_ptr
<BoostParser
> m_parser
;
574 void SetFilename(const std::string
&name
)
579 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
581 if( !m_filename
.size() )
582 throw runtime_error("Boost output requires a specific output file (-f switch)");
584 if( m_filename
== "-" ) {
586 m_parser
.reset( new BoostParser(cout
) );
589 m_parser
.reset( new BoostParser(m_filename
) );
597 //////////////////////////////////////////////////////////////////////////////
598 // Mode: Output, Type: ldif
600 class LdifOutput
: public OutputBase
602 auto_ptr
<Parser
> m_parser
;
613 void SetFilename(const std::string
&name
)
618 void SetDN(const std::string
&dn
)
623 void SetAttribute(const std::string
&attr
)
628 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
630 if( m_filename
== "-" ) {
633 new RecordParser
<Contact
, LdifStore
>(
634 new LdifStore(cout
, m_baseDN
,
639 new RecordParser
<Contact
, LdifStore
>(
640 new LdifStore(m_filename
, m_baseDN
,
647 //////////////////////////////////////////////////////////////////////////////
648 // Mode: Output, Type: mime
650 class MimeStore
: public AllRecordStore
655 MimeStore(std::ostream
&os
)
661 #define HANDLE_PARSER(tname) \
662 void operator() (const Barry::tname &r) \
664 MimeDump<tname>::Dump(m_os, r); \
667 ALL_KNOWN_PARSER_TYPES
670 class MimeOutput
: public OutputBase
672 auto_ptr
<std::ofstream
> m_file
;
673 auto_ptr
<Parser
> m_parser
;
674 std::string m_filename
;
678 : m_filename("-") // default to stdout
682 void SetFilename(const std::string
&name
)
687 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
689 if( m_filename
== "-" ) {
690 m_parser
.reset( new AllRecordParser(cout
,
691 new HexDumpParser(cout
),
692 new MimeStore(cout
)) );
695 m_file
.reset( new std::ofstream(m_filename
.c_str()) );
696 m_parser
.reset( new AllRecordParser(*m_file
,
697 new HexDumpParser(*m_file
),
698 new MimeStore(*m_file
)) );
704 //////////////////////////////////////////////////////////////////////////////
705 // Mode: Output, Type: dump
707 class DumpOutput
: public OutputBase
709 auto_ptr
<Parser
> m_parser
;
723 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
726 m_parser
.reset( new HexDumpParser(cout
) );
729 m_parser
.reset( new AllRecordParser(cout
,
730 new HexDumpParser(cout
),
731 new AllRecordDumpStore(cout
)) );
737 //////////////////////////////////////////////////////////////////////////////
738 // Mode: Output, Type: sha1
740 class Sha1Output
: public OutputBase
742 auto_ptr
<Parser
> m_parser
;
747 : m_include_ids(false)
753 m_include_ids
= true;
756 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
758 m_parser
.reset( new ChecksumParser(m_include_ids
) );
763 //////////////////////////////////////////////////////////////////////////////
764 // Mode: Output, Type: cstore
766 class ContentStoreOutput
: public OutputBase
768 auto_ptr
<Parser
> m_parser
;
770 vector
<string
> m_filenames
;
778 void SetFilename(const std::string
&name
)
780 m_filenames
.push_back(name
);
788 Parser
& GetParser(Barry::Probe
*probe
, IConverter
&ic
)
790 m_parser
.reset( new RecordParser
<ContentStore
, ContentStoreOutput
>(*this) );
795 void operator() (const ContentStore
&rec
)
798 cout
<< rec
.Filename
;
799 if( rec
.FolderFlag
) {
805 // check if this record matches one of the filenames
807 vector
<string
>::iterator i
= find(m_filenames
.begin(),
808 m_filenames
.end(), rec
.Filename
);
809 if( i
!= m_filenames
.end() ) {
815 void SaveFile(const ContentStore
&rec
)
817 size_t slash
= rec
.Filename
.rfind('/');
819 if( slash
== string::npos
)
820 filename
= rec
.Filename
;
822 filename
= rec
.Filename
.substr(slash
+ 1);
824 // modify filename until we find one that doesn't
826 string freshname
= filename
;
828 while( access(freshname
.c_str(), F_OK
) == 0 ) {
830 oss
<< filename
<< count
++;
831 freshname
= oss
.str();
835 cout
<< "Saving: " << rec
.Filename
836 << " as " << freshname
<< endl
;
837 ofstream
ofs(freshname
.c_str());
838 ofs
<< rec
.FileContent
;
841 cout
<< "Error during write!" << endl
;
848 //////////////////////////////////////////////////////////////////////////////
849 // Main application class
854 typedef shared_ptr
<OutputBase
> OutputPtr
;
855 typedef vector
<OutputPtr
> OutputsType
;
858 auto_ptr
<InputBase
> Input
;
863 bool ParseInMode(const string
&mode
);
864 bool ParseOutMode(const string
&mode
);
865 DeviceParser::WriteMode
ParseWriteMode(const std::string
&mode
);
866 // returns true if any of the items in Outputs needs a probe
867 bool OutputsProbeNeeded();
868 int main(int argc
, char *argv
[]);
871 bool App::ParseInMode(const string
&mode
)
873 if( mode
== "device" ) {
874 Input
.reset( new DeviceInput
);
877 else if( mode
== "tar" ) {
878 Input
.reset( new TarInput
);
881 #ifdef __BARRY_BOOST_MODE__
882 else if( mode
== "boost" ) {
883 Input
.reset( new BoostInput
);
887 else if( mode
== "ldif" ) {
888 Input
.reset( new LdifInput
);
891 else if( mode
== "mime" ) {
892 Input
.reset( new MimeInput
);
899 bool App::ParseOutMode(const string
&mode
)
901 if( mode
== "device" ) {
902 Outputs
.push_back( OutputPtr(new DeviceOutput
) );
905 else if( mode
== "tar" ) {
906 Outputs
.push_back( OutputPtr(new TarOutput
) );
909 #ifdef __BARRY_BOOST_MODE__
910 else if( mode
== "boost" ) {
911 Outputs
.push_back( OutputPtr(new BoostOutput
) );
915 else if( mode
== "ldif" ) {
916 Outputs
.push_back( OutputPtr(new LdifOutput
) );
919 else if( mode
== "mime" ) {
920 Outputs
.push_back( OutputPtr(new MimeOutput
) );
923 else if( mode
== "dump" ) {
924 Outputs
.push_back( OutputPtr(new DumpOutput
) );
927 else if( mode
== "sha1" ) {
928 Outputs
.push_back( OutputPtr(new Sha1Output
) );
931 else if( mode
== "cstore" ) {
932 Outputs
.push_back( OutputPtr(new ContentStoreOutput
) );
939 DeviceParser::WriteMode
App::ParseWriteMode(const std::string
&mode
)
941 if( mode
== "erase" )
942 return DeviceParser::ERASE_ALL_WRITE_ALL
;
943 else if( mode
== "overwrite" )
944 return DeviceParser::INDIVIDUAL_OVERWRITE
;
945 else if( mode
== "addonly" )
946 return DeviceParser::ADD_BUT_NO_OVERWRITE
;
947 else if( mode
== "addnew" )
948 return DeviceParser::ADD_WITH_NEW_ID
;
950 throw runtime_error("Unknown device output mode. Must be one of: erase, overwrite, addonly, addnew");
953 bool App::OutputsProbeNeeded()
955 for( OutputsType::iterator i
= Outputs
.begin();
959 if( (*i
)->ProbeNeeded() )
965 int App::main(int argc
, char *argv
[])
967 bool verbose
= false;
968 bool show_parsers
= false, show_fields
= false;
971 // process command line options
972 ModeBase
*current
= 0;
974 int cmd
= getopt(argc
, argv
, "hi:o:nvI:f:p:P:d:D:c:C:ASw:tl");
978 // first option must be in or out, or a global option
993 case 'i': // set input mode
994 // must be first time used
995 if( Input
.get() || !ParseInMode(optarg
) ) {
999 current
= Input
.get();
1002 case 'o': // set output mode
1003 // can be used multiple times
1004 if( !ParseOutMode(optarg
) ) {
1008 current
= Outputs
[Outputs
.size() - 1].get();
1012 case 'c': // set ldif dn
1013 current
->SetDN(optarg
);
1016 case 'C': // set ldif attr
1017 current
->SetAttribute(optarg
);
1020 case 'd': // database name
1021 current
->AddDB(optarg
);
1024 case 'D': // database name to skip
1025 current
->AddSkipDB(optarg
);
1028 case 'f': // filename
1029 current
->SetFilename(optarg
);
1032 case 'p': // device PIN
1033 current
->SetPIN(optarg
);
1036 case 'P': // password
1037 current
->SetPassword(optarg
);
1040 case 'w': // device write mode
1041 current
->SetWriteMode(ParseWriteMode(optarg
));
1044 case 'A': // add all DB names to the device builder
1045 current
->AddAllDBs();
1048 case 't': // include type and IDs in sha1 mode
1049 current
->IncludeIDs();
1052 case 'l': // list only
1056 case 'S': // show parsers and builders
1060 show_parsers
= true;
1063 case 'I': // international charset (iconv)
1064 iconvCharset
= optarg
;
1067 case 'n': // use null hex dump parser only
1068 current
->SetHexDump();
1071 case 'v': // verbose
1082 if( show_parsers
) {
1083 ShowParsers(show_fields
, true);
1088 if( !Input
.get() || !Outputs
.size() ) {
1093 // Initialize the Barry library
1094 Barry::Init(verbose
);
1096 // Create an IConverter object if needed
1097 auto_ptr
<IConverter
> ic
;
1098 if( iconvCharset
.size() ) {
1099 ic
.reset( new IConverter(iconvCharset
.c_str(), true) );
1102 // Probe for devices only if needed
1103 auto_ptr
<Probe
> probe
;
1104 if( Input
->ProbeNeeded() || OutputsProbeNeeded() ) {
1105 // Probe for available devices
1106 probe
.reset( new Probe
);
1109 // Setup the input first (builder)
1110 Builder
&builder
= Input
->GetBuilder(probe
.get(), *ic
);
1112 // Setup a TeeParser with all Outputs
1114 for( OutputsType::iterator i
= Outputs
.begin(); i
!= Outputs
.end(); ++i
) {
1115 Parser
&parser
= (*i
)->GetParser(probe
.get(), *ic
);
1121 pipe
.PumpFile(tee
, ic
.get());
1126 int main(int argc
, char *argv
[])
1130 return app
.main(argc
, argv
);
1132 catch( std::exception
&e
) {
1133 cerr
<< "Exception: " << e
.what() << endl
;