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