bio.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       bio.cc
00003 ///             Barry Input / Output
00004 ///
00005 
00006 /*
00007     Copyright (C) 2010-2012, Net Direct Inc. (http://www.netdirect.ca/)
00008 
00009     This program is free software; you can redistribute it and/or modify
00010     it under the terms of the GNU General Public License as published by
00011     the Free Software Foundation; either version 2 of the License, or
00012     (at your option) any later version.
00013 
00014     This program is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00017 
00018     See the GNU General Public License in the COPYING file at the
00019     root directory of this project for more details.
00020 */
00021 
00022 #include <barry/barry.h>
00023 #include <barry/barrysync.h>
00024 #include <barry/barrybackup.h>
00025 
00026 #include "brecsum.h"
00027 #include "util.h"
00028 
00029 #include <iomanip>
00030 #include <iostream>
00031 #include <sstream>
00032 #include <fstream>
00033 #include <string>
00034 #include <vector>
00035 #include <algorithm>
00036 #include <stdexcept>
00037 #include <tr1/memory>
00038 #include <strings.h>
00039 #include <unistd.h>
00040 
00041 #include "barrygetopt.h"
00042 
00043 using namespace std;
00044 using namespace std::tr1;
00045 using namespace Barry;
00046 
00047 // keeping a record of all the -i device / -o device pin numbers, so
00048 // we can warn the poor user appropriately
00049 std::vector<Barry::Pin> m_device_pins;
00050 
00051 bool IsPinUsed(const Barry::Pin &pin)
00052 {
00053         for( std::vector<Barry::Pin>::const_iterator b = m_device_pins.begin();
00054                         b != m_device_pins.end(); ++b )
00055         {
00056                 if( *b == pin )
00057                         return true;
00058         }
00059         return false;
00060 }
00061 
00062 void Usage()
00063 {
00064    int logical, major, minor;
00065    const char *Version = Barry::Version(logical, major, minor);
00066 
00067    cerr
00068    << "bio - Barry Input / Output\n"
00069    << "      Copyright 2010-2012, Net Direct Inc. (http://www.netdirect.ca/)\n"
00070    << "      Using: " << Version << "\n"
00071    << "      Compiled "
00072 #ifdef __BARRY_BOOST_MODE__
00073    << "with"
00074 #else
00075    << "without"
00076 #endif
00077    << " Boost support\n"
00078    << "\n"
00079    << " Usage:  bio -i <type> [options...]   -o <type> [options...]\n"
00080    << "\n"
00081    << "   -i type   The input type (Builder) to use for producing records\n"
00082    << "             Can be one of: device, tar"
00083 #ifdef __BARRY_BOOST_MODE__
00084    << ", boost"
00085 #endif
00086    << ", ldif, mime\n"
00087    << "   -o type   The output type (Parser) to use for processing records.\n"
00088    << "             Multiple outputs are allowed, as long as they don't\n"
00089    << "             conflict (such as two outputs writing to the same file\n"
00090    << "             or device).\n"
00091    << "             Can be one of: device, tar"
00092 #ifdef __BARRY_BOOST_MODE__
00093    << ", boost"
00094 #endif
00095    << ", ldif, mime, dump, sha1, cstore\n"
00096    << "\n"
00097    << " Options to use for 'device' type:\n"
00098    << "   -d db     Name of input database. Can be used multiple times.\n"
00099    << "   -A        Add all available device databases, instead of specifying\n"
00100    << "             them manually via -d\n"
00101    << "   -p pin    PIN of device to talk to\n"
00102    << "             If only one device is plugged in, this flag is optional\n"
00103    << "   -P pass   Simplistic method to specify device password\n"
00104    << "   -w mode   Set write mode when using 'device' for output.  Must be\n"
00105    << "             specified, or will not write anything.\n"
00106    << "             Can be one of: erase, overwrite, addonly, addnew\n"
00107    << "\n"
00108    << " Options to use for 'tar' backup type:\n"
00109    << "   -d db     Name of input database. Can be used multiple times.\n"
00110    << "             Not available in output mode.  Note that by default,\n"
00111    << "             all databases in the backup are selected, when reading,\n"
00112    << "             unless at least one -d is specified.\n"
00113    << "   -D db     Name of input database to skip.  If no -d options are used,\n"
00114    << "             then all databases are automatically selected.  Using -D\n"
00115    << "             allows a filtering selection.  If -d and -D are used for\n"
00116    << "             the same database, -D takes precedence.\n"
00117    << "   -f file   Tar backup file to read from or write to\n"
00118 #ifdef __BARRY_BOOST_MODE__
00119    << "\n"
00120    << " Options to use for 'boost' type:\n"
00121    << "   -f file   Boost serialization filename to read from or write to\n"
00122    << "             Can use - to specify stdin/stdout\n"
00123 #endif
00124    << "\n"
00125    << " Options to use for 'ldif' type:\n"
00126    << "   -c dn     Convert address book database to LDIF format, using the\n"
00127    << "             specified baseDN\n"
00128    << "   -C dnattr LDIF attribute name to use when building the FQDN\n"
00129    << "             Defaults to 'cn'\n"
00130 /*
00131 LDIF options?
00132 
00133    << "   -L        List Contact field names\n"
00134    << "   -m        Map LDIF name to Contact field / Unmap LDIF name\n"
00135    << "                Map: ldif,read,write - maps ldif to read/write Contact fields\n"
00136    << "                Unmap: ldif name alone\n"
00137    << "   -M        List current LDIF mapping\n"
00138 */
00139    << "\n"
00140    << " Options to use for 'mime' type:\n"
00141    << "   -f file   Filename to read from or write to.  Use - to explicitly\n"
00142    << "             specify stdin/stdout, which is default.\n"
00143    << "\n"
00144    << " Options to use for 'dump' to stdout output type:\n"
00145    << "   -n        Use hex dump parser on all databases.\n"
00146    << "\n"
00147    << " Options to use for 'sha1' sum stdout output type:\n"
00148    << "   -t        Include DB Name, Type, and Unique record IDs in the checksums\n"
00149    << "\n"
00150    << " Options to use for 'cstore' output type:\n"
00151    << "   -l        List filenames only\n"
00152    << "   -f file   Filename from the above list, including path.\n"
00153    << "             If found, the file will be written to the current\n"
00154    << "             directory, using the base filename from the device.\n"
00155    << "\n"
00156    << " Standalone options:\n"
00157    << "   -h        This help\n"
00158    << "   -I cs     International charset for string conversions\n"
00159    << "             Valid values here are available with 'iconv --list'\n"
00160    << "   -S        Show list of supported database parsers and builders.\n"
00161    << "             Use twice to show field names as well.\n"
00162    << "   -v        Dump protocol data during operation\n"
00163    << "\n"
00164    << endl;
00165 }
00166 
00167 class ModeBase
00168 {
00169 public:
00170         virtual ~ModeBase() {}
00171 
00172         virtual bool ProbeNeeded() const { return false; }
00173 
00174         virtual void SetFilename(const std::string &name)
00175         {
00176                 throw runtime_error("Filename not applicable for this mode");
00177         }
00178 
00179         virtual void AddDB(const std::string &dbname)
00180         {
00181                 throw runtime_error("DB not applicable for this mode");
00182         }
00183 
00184         virtual void AddSkipDB(const std::string &dbname)
00185         {
00186                 throw runtime_error("DB skipping not applicable for this mode");
00187         }
00188 
00189         virtual void AddAllDBs()
00190         {
00191                 throw runtime_error("DBs not applicable for this mode");
00192         }
00193 
00194         virtual void SetPIN(const std::string &pin)
00195         {
00196                 throw runtime_error("PIN not applicable for this mode");
00197         }
00198 
00199         virtual void SetPassword(const std::string &password)
00200         {
00201                 throw runtime_error("Password not applicable for this mode");
00202         }
00203 
00204         virtual void SetWriteMode(DeviceParser::WriteMode mode)
00205         {
00206                 throw runtime_error("Device write behaviour not applicable for this mode");
00207         }
00208 
00209         virtual void SetDN(const std::string &dn)
00210         {
00211                 throw runtime_error("DN not applicable for this mode");
00212         }
00213 
00214         virtual void SetAttribute(const std::string &attr)
00215         {
00216                 throw runtime_error("Attribute not applicable for this mode");
00217         }
00218 
00219         virtual void SetHexDump()
00220         {
00221                 throw runtime_error("No hex dump option in this mode");
00222         }
00223 
00224         virtual void IncludeIDs()
00225         {
00226                 throw runtime_error("Including record IDs in the SHA1 sum is not applicable in this mode");
00227         }
00228 
00229         virtual void SetList()
00230         {
00231                 throw runtime_error("List option not applicable for this mode");
00232         }
00233 };
00234 
00235 class DeviceBase : public virtual ModeBase
00236 {
00237 protected:
00238         Barry::Pin m_pin;
00239         std::string m_password;
00240 
00241 public:
00242         bool ProbeNeeded() const { return true; }
00243 
00244         void SetPIN(const std::string &pin)
00245         {
00246                 istringstream iss(pin);
00247                 iss >> m_pin;
00248                 if( !m_pin.Valid() )
00249                         throw runtime_error("Invalid PIN: " + pin);
00250         }
00251 
00252         void SetPassword(const std::string &password)
00253         {
00254                 m_password = password;
00255         }
00256 };
00257 
00258 //////////////////////////////////////////////////////////////////////////////
00259 // Base class for Input Mode
00260 
00261 class InputBase : public virtual ModeBase
00262 {
00263 public:
00264         virtual Builder& GetBuilder(Barry::Probe *probe, IConverter &ic) = 0;
00265 };
00266 
00267 class DeviceInputBase : public DeviceBase, public InputBase
00268 {
00269 };
00270 
00271 //////////////////////////////////////////////////////////////////////////////
00272 // Mode: Input, Type: device
00273 
00274 class DeviceInput : public DeviceInputBase
00275 {
00276         auto_ptr<Controller> m_con;
00277         auto_ptr<Mode::Desktop> m_desktop;
00278         auto_ptr<DeviceBuilder> m_builder;
00279         vector<string> m_dbnames;
00280         bool m_add_all;
00281 
00282 public:
00283         DeviceInput()
00284                 : m_add_all(false)
00285         {
00286         }
00287 
00288         void AddDB(const std::string &dbname)
00289         {
00290                 m_dbnames.push_back(dbname);
00291         }
00292 
00293         void AddAllDBs()
00294         {
00295                 m_add_all = true;
00296         }
00297 
00298         Builder& GetBuilder(Barry::Probe *probe, IConverter &ic)
00299         {
00300                 int i = probe->FindActive(m_pin);
00301                 if( i == -1 ) {
00302                         if( m_pin.Valid() )
00303                                 throw runtime_error("PIN not found: " + m_pin.Str());
00304                         else
00305                                 throw runtime_error("PIN not specified, and more than one device exists.");
00306                 }
00307 
00308                 if( IsPinUsed(probe->Get(i).m_pin) ) {
00309                         throw runtime_error("It seems you are trying to use the same device for multiple input or outputs.");
00310                 }
00311                 m_device_pins.push_back(probe->Get(i).m_pin);
00312 
00313                 m_con.reset( new Controller(probe->Get(i)) );
00314                 m_desktop.reset( new Mode::Desktop(*m_con, ic) );
00315                 m_desktop->Open(m_password.c_str());
00316                 m_builder.reset( new DeviceBuilder(*m_desktop) );
00317 
00318                 if( m_add_all ) {
00319                         m_builder->Add(m_desktop->GetDBDB());
00320                 }
00321                 else {
00322                         for( size_t i = 0; i < m_dbnames.size(); i++ ) {
00323                                 if( !m_builder->Add(m_dbnames[i]) )
00324                                         throw runtime_error("Database not found: " + m_dbnames[i]);
00325                         }
00326                 }
00327 
00328                 return *m_builder;
00329         }
00330 };
00331 
00332 //////////////////////////////////////////////////////////////////////////////
00333 // Mode: Input, Type: tar
00334 
00335 class TarInput : public InputBase
00336 {
00337         auto_ptr<Restore> m_restore;
00338         string m_tarpath;
00339         vector<string> m_dbnames, m_dbskips;
00340 
00341 public:
00342         void SetFilename(const std::string &name)
00343         {
00344                 m_tarpath = name;
00345                 if( name == "-" )
00346                         throw runtime_error("Cannot use stdin as tar source file, sorry.");
00347         }
00348 
00349         void AddDB(const std::string &dbname)
00350         {
00351                 m_dbnames.push_back(dbname);
00352         }
00353 
00354         void AddSkipDB(const std::string &dbname)
00355         {
00356                 m_dbskips.push_back(dbname);
00357         }
00358 
00359         Builder& GetBuilder(Barry::Probe *probe, IConverter &ic)
00360         {
00361                 m_restore.reset( new Restore(m_tarpath, true) );
00362                 for( size_t i = 0; i < m_dbnames.size(); i++ ) {
00363                         m_restore->AddDB(m_dbnames[i]);
00364                 }
00365                 for( size_t i = 0; i < m_dbskips.size(); i++ ) {
00366                         m_restore->AddSkipDB(m_dbskips[i]);
00367                 }
00368 
00369                 return *m_restore;
00370         }
00371 };
00372 
00373 //////////////////////////////////////////////////////////////////////////////
00374 // Mode: Input, Type: boost
00375 
00376 #ifdef __BARRY_BOOST_MODE__
00377 class BoostInput : public InputBase
00378 {
00379         auto_ptr<BoostBuilder> m_builder;
00380         string m_filename;
00381 
00382 public:
00383         BoostInput()
00384                 : m_filename("-")       // default to stdin/stdout
00385         {
00386         }
00387 
00388         void SetFilename(const std::string &name)
00389         {
00390                 m_filename = name;
00391         }
00392 
00393         Builder& GetBuilder(Barry::Probe *probe, IConverter &ic)
00394         {
00395                 if( m_filename == "-" ) {
00396                         // use stdin
00397                         m_builder.reset( new BoostBuilder(cin) );
00398                 }
00399                 else {
00400                         m_builder.reset( new BoostBuilder(m_filename) );
00401                 }
00402                 return *m_builder;
00403         }
00404 
00405 };
00406 #endif
00407 
00408 //////////////////////////////////////////////////////////////////////////////
00409 // Mode: Input, Type: ldif
00410 
00411 class LdifInput : public InputBase
00412 {
00413         auto_ptr<Builder> m_builder;
00414         string m_filename;
00415 
00416 public:
00417         LdifInput()
00418                 : m_filename("-")
00419         {
00420         }
00421 
00422         void SetFilename(const std::string &name)
00423         {
00424                 m_filename = name;
00425         }
00426 
00427         Builder& GetBuilder(Barry::Probe *probe, IConverter &ic)
00428         {
00429                 if( m_filename == "-" ) {
00430                         // use stdin
00431                         m_builder.reset(
00432                                 new RecordBuilder<Contact, LdifStore>(
00433                                         new LdifStore(cin)) );
00434                 }
00435                 else {
00436                         m_builder.reset(
00437                                 new RecordBuilder<Contact, LdifStore>(
00438                                         new LdifStore(m_filename)) );
00439                 }
00440                 return *m_builder;
00441         }
00442 
00443 };
00444 
00445 
00446 //////////////////////////////////////////////////////////////////////////////
00447 // Mode: Input, Type: mime
00448 
00449 class MimeInput : public InputBase
00450 {
00451         auto_ptr<MimeBuilder> m_builder;
00452         string m_filename;
00453 
00454 public:
00455         MimeInput()
00456                 : m_filename("-")
00457         {
00458         }
00459 
00460         void SetFilename(const std::string &name)
00461         {
00462                 m_filename = name;
00463         }
00464 
00465         Builder& GetBuilder(Barry::Probe *probe, IConverter &ic)
00466         {
00467                 if( m_filename == "-" ) {
00468                         // use stdin
00469                         m_builder.reset( new MimeBuilder(cin) );
00470                 }
00471                 else {
00472                         m_builder.reset( new MimeBuilder(m_filename) );
00473                 }
00474                 return *m_builder;
00475         }
00476 
00477 };
00478 
00479 //////////////////////////////////////////////////////////////////////////////
00480 // Base class for Output Mode
00481 
00482 class OutputBase : public virtual ModeBase
00483 {
00484 public:
00485         virtual Parser& GetParser(Barry::Probe *probe, IConverter &ic) = 0;
00486 };
00487 
00488 class DeviceOutputBase : public DeviceBase, public OutputBase
00489 {
00490 };
00491 
00492 //////////////////////////////////////////////////////////////////////////////
00493 // Mode: Output, Type: device
00494 
00495 class DeviceOutput : public DeviceOutputBase
00496 {
00497         auto_ptr<Controller> m_con;
00498         auto_ptr<Mode::Desktop> m_desktop;
00499         auto_ptr<DeviceParser> m_parser;
00500         DeviceParser::WriteMode m_mode;
00501 
00502 public:
00503         DeviceOutput()
00504                 : m_mode(DeviceParser::DROP_RECORD)
00505         {
00506         }
00507 
00508         void SetWriteMode(DeviceParser::WriteMode mode)
00509         {
00510                 m_mode = mode;
00511         }
00512 
00513         Parser& GetParser(Barry::Probe *probe, IConverter &ic)
00514         {
00515                 if( m_mode == DeviceParser::DROP_RECORD ) {
00516                         cerr << "Warning: the -w switch was not specified: no data will be written to the device." << endl;
00517                 }
00518 
00519                 int i = probe->FindActive(m_pin);
00520                 if( i == -1 ) {
00521                         if( m_pin.Valid() )
00522                                 throw runtime_error("PIN not found: " + m_pin.Str());
00523                         else
00524                                 throw runtime_error("PIN not specified, and more than one device exists.");
00525                 }
00526 
00527                 if( IsPinUsed(probe->Get(i).m_pin) ) {
00528                         throw runtime_error("It seems you are trying to use the same device for multiple input or outputs.");
00529                 }
00530                 m_device_pins.push_back(probe->Get(i).m_pin);
00531 
00532                 m_con.reset( new Controller(probe->Get(i)) );
00533                 m_desktop.reset( new Mode::Desktop(*m_con, ic) );
00534                 m_desktop->Open(m_password.c_str());
00535                 m_parser.reset( new DeviceParser(*m_desktop, m_mode) );
00536 
00537                 return *m_parser;
00538         }
00539 };
00540 
00541 //////////////////////////////////////////////////////////////////////////////
00542 // Mode: Output, Type: tar
00543 
00544 class TarOutput : public OutputBase
00545 {
00546         auto_ptr<Backup> m_backup;
00547         string m_tarpath;
00548 
00549 public:
00550         void SetFilename(const std::string &name)
00551         {
00552                 m_tarpath = name;
00553                 if( name == "-" )
00554                         throw runtime_error("Cannot use stdout as tar backup file, sorry.");
00555         }
00556 
00557         Parser& GetParser(Barry::Probe *probe, IConverter &ic)
00558         {
00559                 m_backup.reset( new Backup(m_tarpath) );
00560                 return *m_backup;
00561         }
00562 };
00563 
00564 //////////////////////////////////////////////////////////////////////////////
00565 // Mode: Output, Type: boost
00566 
00567 #ifdef __BARRY_BOOST_MODE__
00568 class BoostOutput : public OutputBase
00569 {
00570         auto_ptr<BoostParser> m_parser;
00571         string m_filename;
00572 
00573 public:
00574         void SetFilename(const std::string &name)
00575         {
00576                 m_filename = name;
00577         }
00578 
00579         Parser& GetParser(Barry::Probe *probe, IConverter &ic)
00580         {
00581                 if( !m_filename.size() )
00582                         throw runtime_error("Boost output requires a specific output file (-f switch)");
00583 
00584                 if( m_filename == "-" ) {
00585                         // use stdout
00586                         m_parser.reset( new BoostParser(cout) );
00587                 }
00588                 else {
00589                         m_parser.reset( new BoostParser(m_filename) );
00590                 }
00591                 return *m_parser;
00592         }
00593 
00594 };
00595 #endif
00596 
00597 //////////////////////////////////////////////////////////////////////////////
00598 // Mode: Output, Type: ldif
00599 
00600 class LdifOutput : public OutputBase
00601 {
00602         auto_ptr<Parser> m_parser;
00603         string m_filename;
00604         string m_baseDN;
00605         string m_dnattr;
00606 
00607 public:
00608         LdifOutput()
00609                 : m_filename("-")
00610         {
00611         }
00612 
00613         void SetFilename(const std::string &name)
00614         {
00615                 m_filename = name;
00616         }
00617 
00618         void SetDN(const std::string &dn)
00619         {
00620                 m_baseDN = dn;
00621         }
00622 
00623         void SetAttribute(const std::string &attr)
00624         {
00625                 m_dnattr = attr;
00626         }
00627 
00628         Parser& GetParser(Barry::Probe *probe, IConverter &ic)
00629         {
00630                 if( m_filename == "-" ) {
00631                         // use stdin
00632                         m_parser.reset(
00633                                 new RecordParser<Contact, LdifStore>(
00634                                         new LdifStore(cout, m_baseDN,
00635                                                 m_dnattr)) );
00636                 }
00637                 else {
00638                         m_parser.reset(
00639                                 new RecordParser<Contact, LdifStore>(
00640                                         new LdifStore(m_filename, m_baseDN,
00641                                                 m_dnattr)) );
00642                 }
00643                 return *m_parser;
00644         }
00645 };
00646 
00647 //////////////////////////////////////////////////////////////////////////////
00648 // Mode: Output, Type: mime
00649 
00650 class MimeStore : public AllRecordStore
00651 {
00652         std::ostream &m_os;
00653 
00654 public:
00655         MimeStore(std::ostream &os)
00656                 : m_os(os)
00657         {
00658         }
00659 
00660 #undef HANDLE_PARSER
00661 #define HANDLE_PARSER(tname) \
00662         void operator() (const Barry::tname &r) \
00663         { \
00664                 MimeDump<tname>::Dump(m_os, r); \
00665         }
00666 
00667         ALL_KNOWN_PARSER_TYPES
00668 };
00669 
00670 class MimeOutput : public OutputBase
00671 {
00672         auto_ptr<std::ofstream> m_file;
00673         auto_ptr<Parser> m_parser;
00674         std::string m_filename;
00675 
00676 public:
00677         MimeOutput()
00678                 : m_filename("-")       // default to stdout
00679         {
00680         }
00681 
00682         void SetFilename(const std::string &name)
00683         {
00684                 m_filename = name;
00685         }
00686 
00687         Parser& GetParser(Barry::Probe *probe, IConverter &ic)
00688         {
00689                 if( m_filename == "-" ) {
00690                         m_parser.reset( new AllRecordParser(cout,
00691                                 new HexDumpParser(cout),
00692                                 new MimeStore(cout)) );
00693                 }
00694                 else {
00695                         m_file.reset( new std::ofstream(m_filename.c_str()) );
00696                         m_parser.reset( new AllRecordParser(*m_file,
00697                                 new HexDumpParser(*m_file),
00698                                 new MimeStore(*m_file)) );
00699                 }
00700                 return *m_parser;
00701         }
00702 };
00703 
00704 //////////////////////////////////////////////////////////////////////////////
00705 // Mode: Output, Type: dump
00706 
00707 class DumpOutput : public OutputBase
00708 {
00709         auto_ptr<Parser> m_parser;
00710         bool m_hex_only;
00711 
00712 public:
00713         DumpOutput()
00714                 : m_hex_only(false)
00715         {
00716         }
00717 
00718         void SetHexDump()
00719         {
00720                 m_hex_only = true;
00721         }
00722 
00723         Parser& GetParser(Barry::Probe *probe, IConverter &ic)
00724         {
00725                 if( m_hex_only ) {
00726                         m_parser.reset( new HexDumpParser(cout) );
00727                 }
00728                 else {
00729                         m_parser.reset( new AllRecordParser(cout,
00730                                 new HexDumpParser(cout),
00731                                 new AllRecordDumpStore(cout)) );
00732                 }
00733                 return *m_parser;
00734         }
00735 };
00736 
00737 //////////////////////////////////////////////////////////////////////////////
00738 // Mode: Output, Type: sha1
00739 
00740 class Sha1Output : public OutputBase
00741 {
00742         auto_ptr<Parser> m_parser;
00743         bool m_include_ids;
00744 
00745 public:
00746         Sha1Output()
00747                 : m_include_ids(false)
00748         {
00749         }
00750 
00751         void IncludeIDs()
00752         {
00753                 m_include_ids = true;
00754         }
00755 
00756         Parser& GetParser(Barry::Probe *probe, IConverter &ic)
00757         {
00758                 m_parser.reset( new ChecksumParser(m_include_ids) );
00759                 return *m_parser;
00760         }
00761 };
00762 
00763 //////////////////////////////////////////////////////////////////////////////
00764 // Mode: Output, Type: cstore
00765 
00766 class ContentStoreOutput : public OutputBase
00767 {
00768         auto_ptr<Parser> m_parser;
00769         bool m_list_only;
00770         vector<string> m_filenames;
00771 
00772 public:
00773         ContentStoreOutput()
00774                 : m_list_only(false)
00775         {
00776         }
00777 
00778         void SetFilename(const std::string &name)
00779         {
00780                 m_filenames.push_back(name);
00781         }
00782 
00783         void SetList()
00784         {
00785                 m_list_only = true;
00786         }
00787 
00788         Parser& GetParser(Barry::Probe *probe, IConverter &ic)
00789         {
00790                 m_parser.reset( new RecordParser<ContentStore, ContentStoreOutput>(*this) );
00791                 return *m_parser;
00792         }
00793 
00794         // storage operator
00795         void operator() (const ContentStore &rec)
00796         {
00797                 if( m_list_only ) {
00798                         cout << rec.Filename;
00799                         if( rec.FolderFlag ) {
00800                                 cout << " (folder)";
00801                         }
00802                         cout << endl;
00803                 }
00804                 else {
00805                         // check if this record matches one of the filenames
00806                         // in the list
00807                         vector<string>::iterator i = find(m_filenames.begin(),
00808                                 m_filenames.end(), rec.Filename);
00809                         if( i != m_filenames.end() ) {
00810                                 SaveFile(rec);
00811                         }
00812                 }
00813         }
00814 
00815         void SaveFile(const ContentStore &rec)
00816         {
00817                 size_t slash = rec.Filename.rfind('/');
00818                 string filename;
00819                 if( slash == string::npos )
00820                         filename = rec.Filename;
00821                 else
00822                         filename = rec.Filename.substr(slash + 1);
00823 
00824                 // modify filename until we find one that doesn't
00825                 // already exist
00826                 string freshname = filename;
00827                 int count = 0;
00828                 while( access(freshname.c_str(), F_OK) == 0 ) {
00829                         ostringstream oss;
00830                         oss << filename << count++;
00831                         freshname = oss.str();
00832                 }
00833 
00834                 // open and write!
00835                 cout << "Saving: " << rec.Filename
00836                         << " as " << freshname << endl;
00837                 ofstream ofs(freshname.c_str());
00838                 ofs << rec.FileContent;
00839                 ofs.flush();
00840                 if( !ofs ) {
00841                         cout << "Error during write!" << endl;
00842                 }
00843         }
00844 };
00845 
00846 
00847 
00848 //////////////////////////////////////////////////////////////////////////////
00849 // Main application class
00850 
00851 class App
00852 {
00853 public:
00854         typedef shared_ptr<OutputBase> OutputPtr;
00855         typedef vector<OutputPtr> OutputsType;
00856 
00857 private:
00858         auto_ptr<InputBase> Input;
00859         OutputsType Outputs;
00860 
00861 public:
00862 
00863         bool ParseInMode(const string &mode);
00864         bool ParseOutMode(const string &mode);
00865         DeviceParser::WriteMode ParseWriteMode(const std::string &mode);
00866         // returns true if any of the items in Outputs needs a probe
00867         bool OutputsProbeNeeded();
00868         int main(int argc, char *argv[]);
00869 };
00870 
00871 bool App::ParseInMode(const string &mode)
00872 {
00873         if( mode == "device" ) {
00874                 Input.reset( new DeviceInput );
00875                 return true;
00876         }
00877         else if( mode == "tar" ) {
00878                 Input.reset( new TarInput );
00879                 return true;
00880         }
00881 #ifdef __BARRY_BOOST_MODE__
00882         else if( mode == "boost" ) {
00883                 Input.reset( new BoostInput );
00884                 return true;
00885         }
00886 #endif
00887         else if( mode == "ldif" ) {
00888                 Input.reset( new LdifInput );
00889                 return true;
00890         }
00891         else if( mode == "mime" ) {
00892                 Input.reset( new MimeInput );
00893                 return true;
00894         }
00895         else
00896                 return false;
00897 }
00898 
00899 bool App::ParseOutMode(const string &mode)
00900 {
00901         if( mode == "device" ) {
00902                 Outputs.push_back( OutputPtr(new DeviceOutput) );
00903                 return true;
00904         }
00905         else if( mode == "tar" ) {
00906                 Outputs.push_back( OutputPtr(new TarOutput) );
00907                 return true;
00908         }
00909 #ifdef __BARRY_BOOST_MODE__
00910         else if( mode == "boost" ) {
00911                 Outputs.push_back( OutputPtr(new BoostOutput) );
00912                 return true;
00913         }
00914 #endif
00915         else if( mode == "ldif" ) {
00916                 Outputs.push_back( OutputPtr(new LdifOutput) );
00917                 return true;
00918         }
00919         else if( mode == "mime" ) {
00920                 Outputs.push_back( OutputPtr(new MimeOutput) );
00921                 return true;
00922         }
00923         else if( mode == "dump" ) {
00924                 Outputs.push_back( OutputPtr(new DumpOutput) );
00925                 return true;
00926         }
00927         else if( mode == "sha1" ) {
00928                 Outputs.push_back( OutputPtr(new Sha1Output) );
00929                 return true;
00930         }
00931         else if( mode == "cstore" ) {
00932                 Outputs.push_back( OutputPtr(new ContentStoreOutput) );
00933                 return true;
00934         }
00935         else
00936                 return false;
00937 }
00938 
00939 DeviceParser::WriteMode App::ParseWriteMode(const std::string &mode)
00940 {
00941         if( mode == "erase" )
00942                 return DeviceParser::ERASE_ALL_WRITE_ALL;
00943         else if( mode == "overwrite" )
00944                 return DeviceParser::INDIVIDUAL_OVERWRITE;
00945         else if( mode == "addonly" )
00946                 return DeviceParser::ADD_BUT_NO_OVERWRITE;
00947         else if( mode == "addnew" )
00948                 return DeviceParser::ADD_WITH_NEW_ID;
00949         else
00950                 throw runtime_error("Unknown device output mode. Must be one of: erase, overwrite, addonly, addnew");
00951 }
00952 
00953 bool App::OutputsProbeNeeded()
00954 {
00955         for( OutputsType::iterator i = Outputs.begin();
00956                 i != Outputs.end();
00957                 ++i )
00958         {
00959                 if( (*i)->ProbeNeeded() )
00960                         return true;
00961         }
00962         return false;
00963 }
00964 
00965 int App::main(int argc, char *argv[])
00966 {
00967         bool verbose = false;
00968         bool show_parsers = false, show_fields = false;
00969         string iconvCharset;
00970 
00971         // process command line options
00972         ModeBase *current = 0;
00973         for(;;) {
00974                 int cmd = getopt(argc, argv, "hi:o:nvI:f:p:P:d:D:c:C:ASw:tl");
00975                 if( cmd == -1 )
00976                         break;
00977 
00978                 // first option must be in or out, or a global option
00979                 if( !current ) {
00980                         if( cmd != 'i' && \
00981                             cmd != 'o' && \
00982                             cmd != 'S' && \
00983                             cmd != 'I' && \
00984                             cmd != 'v' )
00985                         {
00986                                 Usage();
00987                                 return 1;
00988                         }
00989                 }
00990 
00991                 switch( cmd )
00992                 {
00993                 case 'i':       // set input mode
00994                         // must be first time used
00995                         if( Input.get() || !ParseInMode(optarg) ) {
00996                                 Usage();
00997                                 return 1;
00998                         }
00999                         current = Input.get();
01000                         break;
01001 
01002                 case 'o':       // set output mode
01003                         // can be used multiple times
01004                         if( !ParseOutMode(optarg) ) {
01005                                 Usage();
01006                                 return 1;
01007                         }
01008                         current = Outputs[Outputs.size() - 1].get();
01009                         break;
01010 
01011 
01012                 case 'c':       // set ldif dn
01013                         current->SetDN(optarg);
01014                         break;
01015 
01016                 case 'C':       // set ldif attr
01017                         current->SetAttribute(optarg);
01018                         break;
01019 
01020                 case 'd':       // database name
01021                         current->AddDB(optarg);
01022                         break;
01023 
01024                 case 'D':       // database name to skip
01025                         current->AddSkipDB(optarg);
01026                         break;
01027 
01028                 case 'f':       // filename
01029                         current->SetFilename(optarg);
01030                         break;
01031 
01032                 case 'p':       // device PIN
01033                         current->SetPIN(optarg);
01034                         break;
01035 
01036                 case 'P':       // password
01037                         current->SetPassword(optarg);
01038                         break;
01039 
01040                 case 'w':       // device write mode
01041                         current->SetWriteMode(ParseWriteMode(optarg));
01042                         break;
01043 
01044                 case 'A':       // add all DB names to the device builder
01045                         current->AddAllDBs();
01046                         break;
01047 
01048                 case 't':       // include type and IDs in sha1 mode
01049                         current->IncludeIDs();
01050                         break;
01051 
01052                 case 'l':       // list only
01053                         current->SetList();
01054                         break;
01055 
01056                 case 'S':       // show parsers and builders
01057                         if( show_parsers )
01058                                 show_fields = true;
01059                         else
01060                                 show_parsers = true;
01061                         break;
01062 
01063                 case 'I':       // international charset (iconv)
01064                         iconvCharset = optarg;
01065                         break;
01066 
01067                 case 'n':       // use null hex dump parser only
01068                         current->SetHexDump();
01069                         break;
01070 
01071                 case 'v':       // verbose
01072                         verbose = true;
01073                         break;
01074 
01075                 case 'h':       // help
01076                 default:
01077                         Usage();
01078                         return 0;
01079                 }
01080         }
01081 
01082         if( show_parsers ) {
01083                 ShowParsers(show_fields, true);
01084                 ShowBuilders();
01085                 return 0;
01086         }
01087 
01088         if( !Input.get() || !Outputs.size() ) {
01089                 Usage();
01090                 return 0;
01091         }
01092 
01093         // Initialize the Barry library
01094         Barry::Init(verbose);
01095 
01096         // Create an IConverter object if needed
01097         auto_ptr<IConverter> ic;
01098         if( iconvCharset.size() ) {
01099                 ic.reset( new IConverter(iconvCharset.c_str(), true) );
01100         }
01101 
01102         // Probe for devices only if needed
01103         auto_ptr<Probe> probe;
01104         if( Input->ProbeNeeded() || OutputsProbeNeeded() ) {
01105                 // Probe for available devices
01106                 probe.reset( new Probe );
01107         }
01108 
01109         // Setup the input first (builder)
01110         Builder &builder = Input->GetBuilder(probe.get(), *ic);
01111 
01112         // Setup a TeeParser with all Outputs
01113         TeeParser tee;
01114         for( OutputsType::iterator i = Outputs.begin(); i != Outputs.end(); ++i ) {
01115                 Parser &parser = (*i)->GetParser(probe.get(), *ic);
01116                 tee.Add(parser);
01117         }
01118 
01119         // Setup the pipe
01120         Pipe pipe(builder);
01121         pipe.PumpFile(tee, ic.get());
01122 
01123         return 0;
01124 }
01125 
01126 int main(int argc, char *argv[])
01127 {
01128         try {
01129                 App app;
01130                 return app.main(argc, argv);
01131         }
01132         catch( std::exception &e ) {
01133                 cerr << "Exception: " << e.what() << endl;
01134                 return 1;
01135         }
01136 }
01137