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