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