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