self._card_a_prefix = mp self._card_a_vol = vol['vol'] if verbose: print "FBSD: card a = ", self._card_a_prefix if mtd == 2: self._card_b_prefix = mp self._card_b_vol = vol['vol'] if verbose: print "FBSD: card b = ", self._card_b_prefix # Note that mtd is used as a bool... not incrementing is fine. break mtd += 1 if mtd > 0: return True raise DeviceError(_('Unable to mount the device')) # # ------------------------------------------------------ # # this one is pretty simple: # just umount each of the previously # mounted filesystems, using the stored volume object # def eject_freebsd(self): import dbus # There should be some way to access the -v arg... verbose = False if self._main_prefix:
def read_device_collections(self, connection, source_id, dbpath): sequence_min = self.get_database_min_id(source_id) sequence_max = sequence_min sequence_dirty = 0 debug_print("Collection Sequence Min: %d, Source Id: %d" % (sequence_min, source_id)) try: cursor = connection.cursor() # Get existing collections query = 'SELECT _id, tagname FROM tags' cursor.execute(query) except Exception: import traceback tb = traceback.format_exc() raise DeviceError(( ('The Paladin database is corrupted. ' ' Delete the file %s on your reader and then disconnect ' ' reconnect it. If you are using an SD card, you ' ' should delete the file on the card as well. Note that ' ' deleting this file will cause your reader to forget ' ' any notes/highlights, etc.') % dbpath) + ' Underlying error:' '\n' + tb) db_collections = {} for i, row in enumerate(cursor): db_collections[row[1]] = row[0] if row[0] < sequence_min: sequence_dirty = 1 else: sequence_max = max(sequence_max, row[0]) # If the database is 'dirty', then we should fix up the Ids and the sequence number if sequence_dirty == 1: debug_print("Collection Sequence Dirty for Source Id: %d" % source_id) sequence_max = sequence_max + 1 for collection, collectionId in db_collections.items(): if collectionId < sequence_min: # Record the new Id and write it to the DB db_collections[collection] = sequence_max sequence_max = sequence_max + 1 # Fix the collection DB query = 'UPDATE tags SET _id = ? WHERE tagname = ?' t = ( db_collections[collection], collection, ) cursor.execute(query, t) # Fix any references in existing collections query = 'UPDATE booktags SET tag_id = ? WHERE tag_id = ?' t = ( db_collections[collection], collectionId, ) cursor.execute(query, t) self.set_database_sequence_id(connection, 'tags', sequence_max) debug_print("Collection Sequence Max: %d, Source Id: %d" % (sequence_max, source_id)) # Fix up the collections table now... sequence_dirty = 0 sequence_max = sequence_min debug_print("Collections Sequence Min: %d, Source Id: %d" % (sequence_min, source_id)) query = 'SELECT _id FROM booktags' cursor.execute(query) db_collection_pairs = [] for i, row in enumerate(cursor): db_collection_pairs.append(row[0]) if row[0] < sequence_min: sequence_dirty = 1 else: sequence_max = max(sequence_max, row[0]) if sequence_dirty == 1: debug_print("Collections Sequence Dirty for Source Id: %d" % source_id) sequence_max = sequence_max + 1 for pairId in db_collection_pairs: if pairId < sequence_min: # Record the new Id and write it to the DB query = 'UPDATE booktags SET _id = ? WHERE _id = ?' t = ( sequence_max, pairId, ) cursor.execute(query, t) sequence_max = sequence_max + 1 self.set_database_sequence_id(connection, 'booktags', sequence_max) debug_print("Collections Sequence Max: %d, Source Id: %d" % (sequence_max, source_id)) cursor.close() return db_collections
def __init__(self, paths, ext_paths, prefixes, use_author_sort): from lxml import etree if DEBUG: debug_print('Building XMLCache...', paths) self.paths = paths self.prefixes = prefixes self.use_author_sort = use_author_sort # Parse XML files {{{ parser = etree.XMLParser(recover=True) self.roots = {} for source_id, path in paths.items(): if source_id == 0: if not os.path.exists(path): raise DeviceError( ('The SONY XML cache %r does not exist. Try' ' disconnecting and reconnecting your reader.') % repr(path)) with lopen(path, 'rb') as f: raw = f.read() else: raw = EMPTY_CARD_CACHE if os.access(path, os.R_OK): with lopen(path, 'rb') as f: raw = f.read() self.roots[source_id] = etree.fromstring( xml_to_unicode(raw, strip_encoding_pats=True, assume_utf8=True, verbose=DEBUG)[0], parser=parser) if self.roots[source_id] is None: raise Exception( ('The SONY database at %r is corrupted. Try ' ' disconnecting and reconnecting your reader.') % path) self.ext_paths, self.ext_roots = {}, {} for source_id, path in ext_paths.items(): if not os.path.exists(path): try: with lopen(path, 'wb') as f: f.write(EMPTY_EXT_CACHE) fsync(f) except: pass if os.access(path, os.W_OK): try: with lopen(path, 'rb') as f: self.ext_roots[source_id] = etree.fromstring( xml_to_unicode(f.read(), strip_encoding_pats=True, assume_utf8=True, verbose=DEBUG)[0], parser=parser) self.ext_paths[source_id] = path except: pass # }}} recs = self.roots[0].xpath('//*[local-name()="records"]') if not recs: raise DeviceError('The SONY XML database is corrupted (no' ' <records>). Try disconnecting an reconnecting' ' your reader.') self.record_roots = {} self.record_roots.update(self.roots) self.record_roots[0] = recs[0] self.detect_namespaces() debug_print('Done building XMLCache...')
def open_freebsd(self): import dbus # There should be some way to access the -v arg... verbose = False # this gives us access to the S/N, etc. of the reader that the scanner has found # and the match routines for some of that data, like s/n, vendor ID, etc. d = self.detected_device if not d.serial: raise DeviceError("Device has no S/N. Can't continue") return False vols = [] bus = dbus.SystemBus() manager = dbus.Interface( bus.get_object('org.freedesktop.Hal', '/org/freedesktop/Hal/Manager'), 'org.freedesktop.Hal.Manager') paths = manager.FindDeviceStringMatch('usb.serial', d.serial) for path in paths: objif = dbus.Interface(bus.get_object('org.freedesktop.Hal', path), 'org.freedesktop.Hal.Device') # Extra paranoia... try: if d.idVendor == objif.GetProperty('usb.vendor_id') and \ d.idProduct == objif.GetProperty('usb.product_id') and \ d.manufacturer == objif.GetProperty('usb.vendor') and \ d.product == objif.GetProperty('usb.product') and \ d.serial == objif.GetProperty('usb.serial'): midpath = manager.FindDeviceStringMatch( 'info.parent', path) dpaths = manager.FindDeviceStringMatch( 'storage.originating_device', path) + manager.FindDeviceStringMatch( 'storage.originating_device', midpath[0]) for dpath in dpaths: # devif = dbus.Interface(bus.get_object('org.freedesktop.Hal', dpath), 'org.freedesktop.Hal.Device') try: vpaths = manager.FindDeviceStringMatch( 'block.storage_device', dpath) for vpath in vpaths: try: vdevif = dbus.Interface( bus.get_object('org.freedesktop.Hal', vpath), 'org.freedesktop.Hal.Device') if not vdevif.GetProperty( 'block.is_volume'): continue if vdevif.GetProperty( 'volume.fsusage') != 'filesystem': continue volif = dbus.Interface( bus.get_object('org.freedesktop.Hal', vpath), 'org.freedesktop.Hal.Device.Volume') pdevif = dbus.Interface( bus.get_object( 'org.freedesktop.Hal', vdevif.GetProperty('info.parent')), 'org.freedesktop.Hal.Device') vol = { 'node': pdevif.GetProperty('block.device'), 'dev': vdevif, 'vol': volif, 'label': vdevif.GetProperty('volume.label') } vols.append(vol) except dbus.exceptions.DBusException as e: print(e) continue except dbus.exceptions.DBusException as e: print(e) continue except dbus.exceptions.DBusException as e: continue def ocmp(x, y): if x['node'] < y['node']: return -1 if x['node'] > y['node']: return 1 return 0 vols.sort(cmp=ocmp) if verbose: print("FBSD: ", vols) mtd = 0 for vol in vols: mp = '' if vol['dev'].GetProperty('volume.is_mounted'): mp = vol['dev'].GetProperty('volume.mount_point') else: try: vol['vol'].Mount('Calibre-' + vol['label'], vol['dev'].GetProperty('volume.fstype'), []) loops = 0 while not vol['dev'].GetProperty('volume.is_mounted'): time.sleep(1) loops += 1 if loops > 100: print( "ERROR: Timeout waiting for mount to complete") continue mp = vol['dev'].GetProperty('volume.mount_point') except dbus.exceptions.DBusException as e: print("Failed to mount ", e) continue # Mount Point becomes Mount Path mp += '/' if verbose: print("FBSD: mounted", vol['label'], "on", mp) if mtd == 0: self._main_prefix = mp self._main_vol = vol['vol'] if verbose: print("FBSD: main = ", self._main_prefix) if mtd == 1: self._card_a_prefix = mp self._card_a_vol = vol['vol'] if verbose: print("FBSD: card a = ", self._card_a_prefix) if mtd == 2: self._card_b_prefix = mp self._card_b_vol = vol['vol'] if verbose: print("FBSD: card b = ", self._card_b_prefix) # Note that mtd is used as a bool... not incrementing is fine. break mtd += 1 if mtd > 0: return True raise DeviceError(_('Unable to mount the device'))
def read_device_books(self, connection, source_id, dbpath): sequence_min = self.get_database_min_id(source_id) sequence_max = sequence_min sequence_dirty = 0 debug_print("Book Sequence Min: %d, Source Id: %d" % (sequence_min, source_id)) try: cursor = connection.cursor() # Get existing books query = 'SELECT filename, _id FROM books' cursor.execute(query) except Exception: import traceback tb = traceback.format_exc() raise DeviceError(( ('The Paladin database is corrupted. ' ' Delete the file %s on your reader and then disconnect ' ' reconnect it. If you are using an SD card, you ' ' should delete the file on the card as well. Note that ' ' deleting this file will cause your reader to forget ' ' any notes/highlights, etc.') % dbpath) + ' Underlying error:' '\n' + tb) # Get the books themselves, but keep track of any that are less than the minimum. # Record what the max id being used is as well. db_books = {} for i, row in enumerate(cursor): if not hasattr(row[0], 'replace'): continue lpath = row[0].replace('\\', '/') db_books[lpath] = row[1] if row[1] < sequence_min: sequence_dirty = 1 else: sequence_max = max(sequence_max, row[1]) # If the database is 'dirty', then we should fix up the Ids and the sequence number if sequence_dirty == 1: debug_print("Book Sequence Dirty for Source Id: %d" % source_id) sequence_max = sequence_max + 1 for book, bookId in db_books.items(): if bookId < sequence_min: # Record the new Id and write it to the DB db_books[book] = sequence_max sequence_max = sequence_max + 1 # Fix the Books DB query = 'UPDATE books SET _id = ? WHERE filename = ?' t = ( db_books[book], book, ) cursor.execute(query, t) # Fix any references so that they point back to the right book t = ( db_books[book], bookId, ) query = 'UPDATE booktags SET tag_id = ? WHERE tag_id = ?' cursor.execute(query, t) self.set_database_sequence_id(connection, 'books', sequence_max) debug_print("Book Sequence Max: %d, Source Id: %d" % (sequence_max, source_id)) cursor.close() return db_books
def find_device_nodes(self, detected_device=None): def walk(base): base = os.path.abspath(os.path.realpath(base)) for x in os.listdir(base): p = os.path.join(base, x) if os.path.islink(p) or not os.access(p, os.R_OK): continue isfile = os.path.isfile(p) yield p, isfile if not isfile: for y, q in walk(p): yield y, q def raw2num(raw): raw = raw.lower() if not raw.startswith('0x'): raw = '0x' + raw return int(raw, 16) # Find device node based on vendor, product and bcd d, j = os.path.dirname, os.path.join usb_dir = None if detected_device is None: detected_device = self.detected_device def test(val, attr): q = getattr(detected_device, attr) return q == val for x, isfile in walk('/sys/devices'): if isfile and x.endswith('idVendor'): usb_dir = d(x) for y in ('idProduct', 'idVendor', 'bcdDevice'): if not os.access(j(usb_dir, y), os.R_OK): usb_dir = None continue e = lambda q: raw2num(open(j(usb_dir, q)).read()) ven, prod, bcd = map(e, ('idVendor', 'idProduct', 'bcdDevice')) if not (test(ven, 'idVendor') and test(prod, 'idProduct') and test(bcd, 'bcdDevice')): usb_dir = None continue else: break if usb_dir is None: raise DeviceError( _('Unable to detect the %s disk drive.') % self.__class__.__name__) devnodes, ok = [], {} for x, isfile in walk(usb_dir): if not isfile and '/block/' in x: parts = x.split('/') idx = parts.index('block') if idx == len(parts) - 2: sz = j(x, 'size') node = parts[idx + 1] try: exists = int(open(sz).read()) > 0 if exists: node = self.find_largest_partition(x) ok[node] = True else: ok[node] = False except: ok[node] = False if DEBUG and not ok[node]: print( '\nIgnoring the node: %s as could not read size from: %s' % (node, sz)) devnodes.append(node) devnodes += list(repeat(None, 3)) ans = ['/dev/' + x if ok.get(x, False) else None for x in devnodes] ans.sort(key=lambda x: x[5:] if x else 'zzzzz') return self.linux_swap_drives(ans[:3])
def _osx_bsd_names(self): drives = self.osx_get_usb_drives() matches = [] d = self.detected_device if d.serial: for path, vid, pid, bcd, ven, prod, serial in drives: if d.match_serial(serial): matches.append(path) if not matches and d.manufacturer and d.product: for path, vid, pid, bcd, man, prod, serial in drives: if d.match_strings(vid, pid, bcd, man, prod): matches.append(path) if not matches: # Since Apple started mangling the names stored in the IOKit # registry, we cannot trust match_strings() so fallback to matching # on just numbers. See http://www.mobileread.com/forums/showthread.php?t=273213 for path, vid, pid, bcd, man, prod, serial in drives: if d.match_numbers(vid, pid, bcd): matches.append(path) if not matches: from pprint import pformat raise DeviceError( 'Could not detect BSD names for %s. Try rebooting.\nOutput from osx_get_usb_drives():\n%s' % (self.name, pformat(drives))) pat = re.compile(r'(?P<m>\d+)([a-z]+(?P<p>\d+)){0,1}') def nums(x): 'Return (disk num, partition number)' m = pat.search(x) if m is None: return (10000, -1) g = m.groupdict() if g['p'] is None: g['p'] = 0 return map(int, (g.get('m'), g.get('p'))) def dcmp(x, y): ''' Sorting based on the following scheme: - disks without partitions are first - sub sorted based on disk number - disks with partitions are sorted first on disk number, then on partition number ''' x = x.rpartition('/')[-1] y = y.rpartition('/')[-1] x, y = nums(x), nums(y) if x[1] == 0 and y[1] > 0: return cmp(1, 2) if x[1] > 0 and y[1] == 0: return cmp(2, 1) ans = cmp(x[0], y[0]) if ans == 0: ans = cmp(x[1], y[1]) return ans matches.sort(cmp=dcmp) drives = {'main': matches[0]} if len(matches) > 1: drives['carda'] = matches[1] if len(matches) > 2: drives['cardb'] = matches[2] return drives
def open_freebsd(self): import dbus # There should be some way to access the -v arg... verbose = False # this gives us access to the S/N, etc. of the reader that the scanner has found # and the match routines for some of that data, like s/n, vendor ID, etc. d = self.detected_device if not d.serial: raise DeviceError("Device has no S/N. Can't continue") return False vols = [] bus = dbus.SystemBus() manager = dbus.Interface( bus.get_object('org.freedesktop.Hal', '/org/freedesktop/Hal/Manager'), 'org.freedesktop.Hal.Manager') paths = manager.FindDeviceStringMatch('usb.serial', d.serial) for path in paths: objif = dbus.Interface(bus.get_object('org.freedesktop.Hal', path), 'org.freedesktop.Hal.Device') # Extra paranoia... try: if d.idVendor == objif.GetProperty('usb.vendor_id') and \ d.idProduct == objif.GetProperty('usb.product_id') and \ d.manufacturer == objif.GetProperty('usb.vendor') and \ d.product == objif.GetProperty('usb.product') and \ d.serial == objif.GetProperty('usb.serial'): dpaths = manager.FindDeviceStringMatch( 'storage.originating_device', path) for dpath in dpaths: #devif = dbus.Interface(bus.get_object('org.freedesktop.Hal', dpath), 'org.freedesktop.Hal.Device') try: vpaths = manager.FindDeviceStringMatch( 'block.storage_device', dpath) for vpath in vpaths: try: vdevif = dbus.Interface( bus.get_object('org.freedesktop.Hal', vpath), 'org.freedesktop.Hal.Device') if not vdevif.GetProperty( 'block.is_volume'): continue if vdevif.GetProperty( 'volume.fsusage') != 'filesystem': continue volif = dbus.Interface( bus.get_object('org.freedesktop.Hal', vpath), 'org.freedesktop.Hal.Device.Volume') pdevif = dbus.Interface( bus.get_object( 'org.freedesktop.Hal', vdevif.GetProperty('info.parent')), 'org.freedesktop.Hal.Device') vol = { 'node': pdevif.GetProperty('block.device'), 'dev': vdevif, 'vol': volif, 'label': vdevif.GetProperty('volume.label') } vols.append(vol) except dbus.exceptions.DBusException, e: print e continue except dbus.exceptions.DBusException, e: print e continue except dbus.exceptions.DBusException, e: continue
def _osx_bsd_names(self): drives = self.osx_get_usb_drives() matches = [] d = self.detected_device if d.serial: for path, vid, pid, bcd, ven, prod, serial in drives: if d.match_serial(serial): matches.append(path) if not matches: if d.manufacturer and d.product: for path, vid, pid, bcd, man, prod, serial in drives: if d.match_strings(vid, pid, bcd, man, prod): matches.append(path) else: for path, vid, pid, bcd, man, prod, serial in drives: if d.match_numbers(vid, pid, bcd): matches.append(path) if not matches: raise DeviceError( 'Could not detect BSD names for %s. Try rebooting.' % self.name) pat = re.compile(r'(?P<m>\d+)([a-z]+(?P<p>\d+)){0,1}') def nums(x): 'Return (disk num, partition number)' m = pat.search(x) if m is None: return (10000, -1) g = m.groupdict() if g['p'] is None: g['p'] = 0 return map(int, (g.get('m'), g.get('p'))) def dcmp(x, y): ''' Sorting based on the following scheme: - disks without partitions are first - sub sorted based on disk number - disks with partitions are sorted first on disk number, then on partition number ''' x = x.rpartition('/')[-1] y = y.rpartition('/')[-1] x, y = nums(x), nums(y) if x[1] == 0 and y[1] > 0: return cmp(1, 2) if x[1] > 0 and y[1] == 0: return cmp(2, 1) ans = cmp(x[0], y[0]) if ans == 0: ans = cmp(x[1], y[1]) return ans matches.sort(cmp=dcmp) drives = {'main': matches[0]} if len(matches) > 1: drives['carda'] = matches[1] if len(matches) > 2: drives['cardb'] = matches[2] return drives
def open_windows(self): from calibre.devices.scanner import win_pnp_drives, drivecmp time.sleep(5) drives = {} seen = set() prod_pat = re.compile(r'PROD_(.+?)&') dup_prod_id = False def check_for_dups(pnp_id): try: match = prod_pat.search(pnp_id) if match is not None: prodid = match.group(1) if prodid in seen: return True else: seen.add(prodid) except: pass return False for drive, pnp_id in win_pnp_drives().items(): if self.windows_match_device(pnp_id, 'WINDOWS_CARD_A_MEM') and \ not drives.get('carda', False): drives['carda'] = drive dup_prod_id |= check_for_dups(pnp_id) elif self.windows_match_device(pnp_id, 'WINDOWS_CARD_B_MEM') and \ not drives.get('cardb', False): drives['cardb'] = drive dup_prod_id |= check_for_dups(pnp_id) elif self.windows_match_device(pnp_id, 'WINDOWS_MAIN_MEM') and \ not drives.get('main', False): drives['main'] = drive dup_prod_id |= check_for_dups(pnp_id) if 'main' in drives.keys() and 'carda' in drives.keys() and \ 'cardb' in drives.keys(): break # This is typically needed when the device has the same # WINDOWS_MAIN_MEM and WINDOWS_CARD_A_MEM in which case # if the device is connected without a card, the above # will incorrectly identify the main mem as carda # See for example the driver for the Nook if drives.get('carda', None) is not None and \ drives.get('main', None) is None: drives['main'] = drives.pop('carda') if drives.get('main', None) is None: raise DeviceError( _('Unable to detect the %s disk drive. Try rebooting.') % self.__class__.__name__) # Sort drives by their PNP drive numbers if the CARD and MAIN # MEM strings are identical if dup_prod_id or \ self.WINDOWS_MAIN_MEM in (self.WINDOWS_CARD_A_MEM, self.WINDOWS_CARD_B_MEM) or \ self.WINDOWS_CARD_A_MEM == self.WINDOWS_CARD_B_MEM: letters = sorted(drives.values(), cmp=drivecmp) drives = {} for which, letter in zip(['main', 'carda', 'cardb'], letters): drives[which] = letter drives = self.windows_sort_drives(drives) self._main_prefix = drives.get('main') self._card_a_prefix = drives.get('carda', None) self._card_b_prefix = drives.get('cardb', None)