00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #define FUSE_USE_VERSION 25
00023 #include <fuse.h>
00024 #include <fuse_opt.h>
00025
00026 #include <barry/barry.h>
00027 #include <sstream>
00028 #include <getopt.h>
00029 #include <vector>
00030 #include <list>
00031 #include <string>
00032 #include <stdexcept>
00033 #include <memory>
00034 #include <tr1/memory>
00035 #include <errno.h>
00036 #include <sys/types.h>
00037 #include <fcntl.h>
00038 #include <string.h>
00039 #include "i18n.h"
00040
00041 using namespace std;
00042 using namespace std::tr1;
00043 using namespace Barry;
00044
00045
00046 const char *error_log_filename = "error.log";
00047
00048
00049 string cmdline_pin;
00050 string cmdline_password;
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063 void Blurb()
00064 {
00065 int major, minor;
00066 const char *Version = Barry::Version(major, minor);
00067
00068 cerr
00069 << "bfuse - FUSE filesystem for Blackberry databases\n"
00070 << " Copyright 2008-2011, Net Direct Inc. (http://www.netdirect.ca/)\n"
00071 << " Using: " << Version << "\n"
00072 << endl;
00073 }
00074
00075 void Usage()
00076 {
00077 cerr
00078 << "\n"
00079 << "Barry specific options:\n"
00080 << " -p pin PIN of device to talk with\n"
00081 << " If only one device is plugged in, this flag is optional\n"
00082 << " -P pass Simplistic method to specify device password\n"
00083 << endl;
00084
00085
00086
00087
00088
00089
00090
00091 }
00092
00093
00094
00095
00096 class fuse_error : public std::runtime_error
00097 {
00098 int m_errno;
00099 public:
00100 fuse_error(int errno_, const std::string &msg)
00101 : std::runtime_error(msg), m_errno(errno_)
00102 {}
00103
00104 int get_errno() const { return m_errno; }
00105 };
00106
00107
00108
00109
00110
00111 class DataDumpParser : public Barry::Parser
00112 {
00113 uint32_t m_id;
00114 std::ostream &m_os;
00115
00116 public:
00117 explicit DataDumpParser(std::ostream &os)
00118 : m_os(os)
00119 {
00120 }
00121
00122 virtual void ParseRecord(const Barry::DBData &data,
00123 const Barry::IConverter *ic)
00124 {
00125 m_id = data.GetUniqueId();
00126 m_os << "Raw record dump for record: "
00127 << std::hex << m_id << std::endl;
00128 m_os << data.GetData() << std::endl;
00129 }
00130 };
00131
00132 template <class Record>
00133 struct Store
00134 {
00135 std::ostream &m_os;
00136
00137 explicit Store(std::ostream &os)
00138 : m_os(os)
00139 {
00140 }
00141
00142
00143 void operator()(const Record &rec)
00144 {
00145 m_os << rec;
00146 }
00147 };
00148
00149 typedef std::auto_ptr<Barry::Parser> ParserPtr;
00150
00151 ParserPtr GetParser(const string &name, std::ostream &os, bool null_parser)
00152 {
00153 if( null_parser ) {
00154
00155 return ParserPtr( new DataDumpParser(os) );
00156 }
00157
00158 else if( name == Contact::GetDBName() ) {
00159 return ParserPtr(
00160 new RecordParser<Contact, Store<Contact> > (
00161 new Store<Contact>(os)));
00162 }
00163 else if( name == Message::GetDBName() ) {
00164 return ParserPtr(
00165 new RecordParser<Message, Store<Message> > (
00166 new Store<Message>(os)));
00167 }
00168 else if( name == Calendar::GetDBName() ) {
00169 return ParserPtr(
00170 new RecordParser<Calendar, Store<Calendar> > (
00171 new Store<Calendar>(os)));
00172 }
00173 else if( name == CalendarAll::GetDBName() ) {
00174 return ParserPtr(
00175 new RecordParser<CalendarAll, Store<CalendarAll> > (
00176 new Store<CalendarAll>(os)));
00177 }
00178 else if( name == ServiceBook::GetDBName() ) {
00179 return ParserPtr(
00180 new RecordParser<ServiceBook, Store<ServiceBook> > (
00181 new Store<ServiceBook>(os)));
00182 }
00183
00184 else if( name == Memo::GetDBName() ) {
00185 return ParserPtr(
00186 new RecordParser<Memo, Store<Memo> > (
00187 new Store<Memo>(os)));
00188 }
00189 else if( name == Task::GetDBName() ) {
00190 return ParserPtr(
00191 new RecordParser<Task, Store<Task> > (
00192 new Store<Task>(os)));
00193 }
00194 else if( name == PINMessage::GetDBName() ) {
00195 return ParserPtr(
00196 new RecordParser<PINMessage, Store<PINMessage> > (
00197 new Store<PINMessage>(os)));
00198 }
00199 else if( name == SavedMessage::GetDBName() ) {
00200 return ParserPtr(
00201 new RecordParser<SavedMessage, Store<SavedMessage> > (
00202 new Store<SavedMessage>(os)));
00203 }
00204 else if( name == Folder::GetDBName() ) {
00205 return ParserPtr(
00206 new RecordParser<Folder, Store<Folder> > (
00207 new Store<Folder>(os)));
00208 }
00209 else if( name == Timezone::GetDBName() ) {
00210 return ParserPtr(
00211 new RecordParser<Timezone, Store<Timezone> > (
00212 new Store<Timezone>(os)));
00213 }
00214 else {
00215
00216 return ParserPtr( new DataDumpParser(os) );
00217 }
00218 }
00219
00220
00221
00222
00223 class PathSplit
00224 {
00225 std::string m_pin, m_db, m_record, m_field, m_remainder;
00226
00227 int m_level;
00228
00229 bool m_is_root;
00230
00231 public:
00232 explicit PathSplit(const char *path)
00233 : m_level(-1)
00234 , m_is_root(false)
00235 {
00236 if( *path != '/' )
00237 return;
00238
00239 if( *(path+1) == 0 ) {
00240 m_is_root = true;
00241 return;
00242 }
00243
00244 const char *s = path, *e = path;
00245 while( *e ) {
00246 while( *e && *e != '/' )
00247 e++;
00248
00249 m_level++;
00250
00251 if( s != e && (s+1) != e ) {
00252 string token(s+1, e);
00253
00254 switch( m_level )
00255 {
00256 case 0:
00257 m_level = -1;
00258 return;
00259
00260 case 1:
00261 m_pin = token;
00262 break;
00263
00264 case 2:
00265 m_db = token;
00266 break;
00267
00268 case 3:
00269 m_record = token;
00270 break;
00271
00272 case 4:
00273 m_field = token;
00274 break;
00275
00276 default:
00277 m_remainder = s;
00278 return;
00279 }
00280
00281
00282 s = e;
00283 if( *e )
00284 e++;
00285 }
00286 else if( *e ) {
00287
00288 e++;
00289 }
00290 }
00291 }
00292
00293 bool IsRoot() const { return m_is_root; }
00294 const std::string& Pin() const { return m_pin; }
00295 const std::string& DB() const { return m_db; }
00296 const std::string& Record() const { return m_record; }
00297 const std::string& Field() const { return m_field; }
00298 const std::string& Remainder() const { return m_remainder; }
00299 int Level() const { return m_level; }
00300 };
00301
00302
00303
00304
00305
00306 class Entry
00307 {
00308 public:
00309 virtual ~Entry() {}
00310 };
00311
00312 class Directory : public Entry
00313 {
00314 public:
00315 virtual int ReadDir(void *buf, fuse_fill_dir_t filler) = 0;
00316 virtual void FillDirStat(struct stat *st)
00317 {
00318 st->st_mode = S_IFDIR | 0555;
00319 st->st_nlink = 2;
00320 }
00321 };
00322
00323 class File : public Entry
00324 {
00325 public:
00326 virtual void FillFileStat(const char *path, struct stat *st) = 0;
00327 virtual bool AccessOk(int flags)
00328 {
00329
00330 return (flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_RDONLY;
00331 }
00332 virtual int ReadFile(const char *path, char *buf, size_t size, off_t offset) = 0;
00333 };
00334
00335 typedef Directory* DirectoryPtr;
00336 typedef File* FilePtr;
00337 typedef std::string NameT;
00338 typedef std::map<NameT, DirectoryPtr> DirMap;
00339 typedef std::map<NameT, FilePtr> FileMap;
00340
00341 static DirMap g_dirmap;
00342 static FileMap g_filemap;
00343
00344 static Directory* FindDir(const NameT &name)
00345 {
00346 DirMap::iterator di = g_dirmap.find(name);
00347 return di == g_dirmap.end() ? 0 : di->second;
00348 }
00349
00350 static File* FindFile(const NameT &name)
00351 {
00352 FileMap::iterator fi = g_filemap.find(name);
00353 return fi == g_filemap.end() ? 0 : fi->second;
00354 }
00355
00356
00357
00358
00359 class Database : public Directory, public File
00360 {
00361 public:
00362 Barry::Mode::Desktop &m_desk;
00363 std::string m_name;
00364 const Barry::DatabaseItem *m_pdb;
00365
00366 public:
00367 Database(Barry::Mode::Desktop &desktop,
00368 const std::string &pin, const Barry::DatabaseItem *pdb)
00369 : m_desk(desktop)
00370 , m_pdb(pdb)
00371 {
00372 m_name = string("/") + pin + "/" + m_pdb->Name;
00373
00374
00375 g_dirmap[ m_name ] = this;
00376 }
00377
00378 ~Database()
00379 {
00380
00381 FileMap::iterator b = g_filemap.begin(), e = g_filemap.end();
00382 for( ; b != e; ++b ) {
00383 if( b->second == this ) {
00384 g_filemap.erase(b);
00385 }
00386 }
00387
00388
00389 g_dirmap.erase( m_name );
00390 }
00391
00392 void AddFile(const std::string &recordId)
00393 {
00394
00395
00396
00397
00398 string name = m_name + "/" + recordId;
00399 g_filemap[ name ] = this;
00400 }
00401
00402 virtual int ReadDir(void *buf, fuse_fill_dir_t filler)
00403 {
00404 filler(buf, ".", NULL, 0);
00405 filler(buf, "..", NULL, 0);
00406
00407
00408 Barry::RecordStateTable rst;
00409 m_desk.GetRecordStateTable(m_pdb->Number, rst);
00410
00411 Barry::RecordStateTable::StateMapType::iterator
00412 b = rst.StateMap.begin(),
00413 e = rst.StateMap.end();
00414 for( ; b != e; ++ b ) {
00415 ostringstream oss;
00416 oss << hex << b->second.RecordId;
00417 filler(buf, oss.str().c_str(), NULL, 0);
00418
00419 AddFile(oss.str());
00420 }
00421 return 0;
00422 }
00423
00424 virtual void FillFileStat(const char *path, struct stat *st)
00425 {
00426
00427 PathSplit ps(path);
00428
00429 string constructed = string("/") + ps.Pin() + "/" + ps.DB();
00430 if( constructed != m_name ) {
00431
00432 throw std::logic_error("Constructed != name");
00433 }
00434
00435 string data = GetRecordData(ps.Record());
00436
00437 st->st_mode = S_IFREG | 0444;
00438 st->st_nlink = 1;
00439 st->st_size = data.size();
00440 }
00441
00442 virtual int ReadFile(const char *path, char *buf, size_t size, off_t offset)
00443 {
00444
00445 PathSplit ps(path);
00446
00447 string constructed = string("/") + ps.Pin() + "/" + ps.DB();
00448 if( constructed != m_name ) {
00449
00450 throw std::logic_error("Constructed != name");
00451 }
00452
00453 string data = GetRecordData(ps.Record());
00454
00455 size_t len = data.size();
00456 if( offset >= 0 && offset < (off_t)len ) {
00457 if( (offset + size) > len )
00458 size = len - offset;
00459 memcpy(buf, data.data() + offset, size);
00460 }
00461 else {
00462 size = 0;
00463 }
00464 return size;
00465 }
00466
00467 const std::string& GetDBName() const { return m_pdb->Name; }
00468
00469 std::string GetRecordData(const std::string &recordId)
00470 {
00471 string data;
00472
00473 Barry::RecordStateTable rst;
00474 m_desk.GetRecordStateTable(m_pdb->Number, rst);
00475
00476 uint32_t recid = strtoul(recordId.c_str(), NULL, 16);
00477 RecordStateTable::IndexType index;
00478 if( rst.GetIndex(recid, &index) ) {
00479 ostringstream oss;
00480 ParserPtr parser = GetParser(m_pdb->Name, oss, false);
00481 m_desk.GetRecord(m_pdb->Number, index, *parser);
00482 data = oss.str();
00483 }
00484
00485 return data;
00486 }
00487 };
00488
00489 class DesktopCon : public Directory
00490 {
00491 public:
00492 typedef std::tr1::shared_ptr<Database> DatabasePtr;
00493 typedef std::list<DatabasePtr> DBList;
00494 public:
00495 Barry::Controller m_con;
00496 Barry::Mode::Desktop m_desk;
00497 std::string m_pin;
00498 DBList m_dblist;
00499
00500 DesktopCon(const Barry::ProbeResult &result, const std::string &pin)
00501 : m_con(result)
00502 , m_desk(m_con)
00503 , m_pin(pin)
00504 {
00505
00506 g_dirmap[ string("/") + pin ] = this;
00507 }
00508
00509 ~DesktopCon()
00510 {
00511
00512 g_dirmap.erase( string("/") + m_pin );
00513 }
00514
00515 virtual int ReadDir(void *buf, fuse_fill_dir_t filler)
00516 {
00517 filler(buf, ".", NULL, 0);
00518 filler(buf, "..", NULL, 0);
00519
00520
00521 DBList::const_iterator b = m_dblist.begin(), e = m_dblist.end();
00522 for( ; b != e; ++ b ) {
00523 filler(buf, (*b)->GetDBName().c_str(), NULL, 0);
00524 }
00525 return 0;
00526 }
00527
00528 void Open(const char *password = 0)
00529 {
00530
00531 m_desk.Open(password);
00532
00533
00534 DatabaseDatabase::DatabaseArrayType::const_iterator
00535 dbi = m_desk.GetDBDB().Databases.begin(),
00536 dbe = m_desk.GetDBDB().Databases.end();
00537 for( ; dbi != dbe; ++dbi ) {
00538 DatabasePtr db = DatabasePtr(
00539 new Database(m_desk, m_pin, &(*dbi)) );
00540 m_dblist.push_back(db);
00541 }
00542 }
00543 };
00544
00545 class Context : public Directory, public File
00546 {
00547 public:
00548 typedef std::auto_ptr<Barry::Probe> ProbePtr;
00549 typedef std::tr1::shared_ptr<DesktopCon> DesktopConPtr;
00550 typedef std::string PinT;
00551 typedef std::map<PinT, DesktopConPtr> PinMap;
00552
00553 ProbePtr m_probe;
00554 PinMap m_pinmap;
00555
00556 string m_error_log;
00557
00558 string m_limit_pin;
00559 string m_password;
00560
00561 public:
00562 Context(const string &limit_pin = "", const string &password = "")
00563 : m_limit_pin(limit_pin)
00564 , m_password(password)
00565 {
00566 g_dirmap["/"] = this;
00567 g_filemap[string("/") + error_log_filename] = this;
00568
00569 m_error_log = "Hello FUSE world. This is Barry. Pleased to meet you.\n";
00570 }
00571
00572 ~Context()
00573 {
00574 g_dirmap.erase("/");
00575 g_filemap.erase(string("/") + error_log_filename);
00576 }
00577
00578 virtual int ReadDir(void *buf, fuse_fill_dir_t filler)
00579 {
00580 filler(buf, ".", NULL, 0);
00581 filler(buf, "..", NULL, 0);
00582 filler(buf, error_log_filename, NULL, 0);
00583
00584
00585 PinMap::const_iterator b = m_pinmap.begin(), e = m_pinmap.end();
00586 for( ; b != e; ++ b ) {
00587 filler(buf, b->first.c_str(), NULL, 0);
00588 }
00589 return 0;
00590 }
00591
00592 virtual void FillFileStat(const char *path, struct stat *st)
00593 {
00594 st->st_mode = S_IFREG | 0444;
00595 st->st_nlink = 1;
00596 st->st_size = m_error_log.size();
00597 }
00598
00599 virtual int ReadFile(const char *path, char *buf, size_t size, off_t offset)
00600 {
00601 size_t len = m_error_log.size();
00602 if( offset >= 0 && offset < (off_t)len ) {
00603 if( (offset + size) > len )
00604 size = len - offset;
00605 memcpy(buf, m_error_log.data() + offset, size);
00606 }
00607 else {
00608 size = 0;
00609 }
00610 return size;
00611 }
00612
00613 void Log(const std::string &msg)
00614 {
00615 m_error_log += msg;
00616 m_error_log += "\n";
00617 }
00618
00619 const std::string& GetLog() const { return m_error_log; }
00620
00621 void ProbeAll()
00622 {
00623
00624 m_probe.reset( new Probe );
00625
00626
00627 for( int i = 0; i < m_probe->GetCount(); i++ ) {
00628 string curpin = m_probe->Get(i).m_pin.Str();
00629
00630
00631 if( !curpin.size() || m_pinmap.find(curpin) != m_pinmap.end() ) {
00632 continue;
00633 }
00634
00635
00636 if( m_limit_pin.size() && curpin != m_limit_pin ) {
00637 continue;
00638 }
00639
00640 DesktopConPtr dev = DesktopConPtr (
00641 new DesktopCon(m_probe->Get(i), curpin) );
00642 dev->Open(m_password.c_str());
00643 m_pinmap[ curpin ] = dev;
00644 }
00645 }
00646
00647 DesktopCon* FindPin(PinT pin)
00648 {
00649 PinMap::iterator pi = m_pinmap.find(pin);
00650 return pi == m_pinmap.end() ? 0 : pi->second.get();
00651 }
00652 };
00653
00654
00655
00656
00657
00658 static void* bfuse_init()
00659 {
00660
00661
00662 Barry::Init(false);
00663
00664 Context *ctx = 0;
00665
00666 try {
00667 ctx = new Context(cmdline_pin, cmdline_password);
00668 ctx->ProbeAll();
00669 }
00670 catch( std::exception &e ) {
00671 if( ctx ) {
00672 ctx->Log(e.what());
00673 }
00674 }
00675
00676 return ctx;
00677 }
00678
00679 static void bfuse_destroy(void *data)
00680 {
00681 if( data ) {
00682 Context *ctx = (Context*) data;
00683 delete ctx;
00684 }
00685 }
00686
00687 static int bfuse_getattr(const char *path, struct stat *st)
00688 {
00689 memset(st, 0, sizeof(*st));
00690
00691 if( Directory *dir = FindDir(path) ) {
00692 dir->FillDirStat(st);
00693 return 0;
00694 }
00695 else if( File *file = FindFile(path) ) {
00696 file->FillFileStat(path, st);
00697 return 0;
00698 }
00699 else
00700 return -ENOENT;
00701 }
00702
00703 static int bfuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
00704 off_t , struct fuse_file_info * )
00705 {
00706 Directory *dir = FindDir(path);
00707 if( !dir )
00708 return -ENOENT;
00709 return dir->ReadDir(buf, filler);
00710 }
00711
00712 static int bfuse_open(const char *path, struct fuse_file_info *fi)
00713 {
00714 File *file = FindFile(path);
00715 if( !file )
00716 return -ENOENT;
00717
00718 if( !file->AccessOk(fi->flags) )
00719 return -EACCES;
00720
00721 return 0;
00722 }
00723
00724 static int bfuse_read(const char *path, char *buf, size_t size, off_t offset,
00725 struct fuse_file_info *fi)
00726 {
00727 File *file = FindFile(path);
00728 if( !file )
00729 return -ENOENT;
00730
00731 return file->ReadFile(path, buf, size, offset);
00732 }
00733
00734
00735 static struct fuse_operations bfuse_oper;
00736
00737
00738
00739
00740
00741 int main(int argc, char *argv[])
00742 {
00743 INIT_I18N(PACKAGE);
00744
00745 cout.sync_with_stdio(true);
00746
00747
00748 Blurb();
00749
00750
00751 bfuse_oper.init = bfuse_init;
00752 bfuse_oper.destroy = bfuse_destroy;
00753 bfuse_oper.getattr = bfuse_getattr;
00754 bfuse_oper.readdir = bfuse_readdir;
00755 bfuse_oper.open = bfuse_open;
00756 bfuse_oper.read = bfuse_read;
00757
00758
00759
00760
00761
00762 int fuse_argc = 0;
00763 char **fuse_argv = new char*[argc];
00764
00765 for( int i = 0; i < argc; i++ ) {
00766 if( argv[i][0] == '-' ) {
00767
00768 switch( argv[i][1] )
00769 {
00770
00771
00772
00773
00774
00775
00776
00777
00778 case 'p':
00779 if( i+1 < argc ) {
00780 cmdline_pin = argv[++i];
00781 }
00782 continue;
00783
00784 case 'P':
00785 if( i+1 < argc ) {
00786 cmdline_password = argv[++i];
00787 }
00788 continue;
00789
00790 case 'h':
00791 Usage();
00792 break;
00793 }
00794 }
00795
00796
00797 fuse_argv[fuse_argc] = argv[i];
00798 fuse_argc++;
00799 }
00800
00801 int ret = fuse_main(fuse_argc, fuse_argv, &bfuse_oper);
00802 delete [] fuse_argv;
00803 return ret;
00804 }
00805