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