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