Exemplo n.º 1
0
    def open_linux(self):
        def mount(node, type):
            mp = self.node_mountpoint(node)
            if mp is not None:
                return mp, 0

            def do_mount(node):
                try:
                    from calibre.devices.udisks import mount
                    mount(node)
                    return 0
                except:
                    print 'Udisks mount call failed:'
                    import traceback
                    traceback.print_exc()
                    return 1

            ret = do_mount(node)
            if ret != 0:
                return None, ret
            return self.node_mountpoint(node) + '/', 0

        main, carda, cardb = self.find_device_nodes()
        if main is None:
            raise DeviceError(
                _('Unable to detect the %s disk drive. Either '
                  'the device has already been ejected, or your '
                  'kernel is exporting a deprecated version of SYSFS.') %
                self.__class__.__name__)

        self._linux_mount_map = {}
        mp, ret = mount(main, 'main')
        if mp is None:
            raise DeviceError(
                _('Unable to mount main memory (Error code: %d)') % ret)
        if not mp.endswith('/'): mp += '/'
        self._linux_mount_map[main] = mp
        self._main_prefix = mp
        self._linux_main_device_node = main
        cards = [(carda, '_card_a_prefix', 'carda'),
                 (cardb, '_card_b_prefix', 'cardb')]
        for card, prefix, typ in cards:
            if card is None: continue
            mp, ret = mount(card, typ)
            if mp is None:
                print >> sys.stderr, 'Unable to mount card (Error code: %d)' % ret
            else:
                if not mp.endswith('/'): mp += '/'
                setattr(self, prefix, mp)
                self._linux_mount_map[card] = mp

        self.filter_read_only_mount_points()
Exemplo n.º 2
0
    def open_osx(self):
        drives = self.osx_bsd_names()
        bsd_drives = dict(**drives)
        drives = self.osx_sort_names(drives)
        mount_map = usbobserver.get_mounted_filesystems()
        for k, v in drives.items():
            drives[k] = mount_map.get(v, None)
        if drives['main'] is None:
            print bsd_drives, mount_map, drives
            raise DeviceError(
                _('Unable to detect the %s mount point. Try rebooting.') %
                self.__class__.__name__)
        pat = self.OSX_MAIN_MEM_VOL_PAT
        if pat is not None and len(drives) > 1 and 'main' in drives:
            if pat.search(drives['main']) is None:
                main = drives['main']
                for x in ('carda', 'cardb'):
                    if x in drives and pat.search(drives[x]):
                        drives['main'] = drives.pop(x)
                        drives[x] = main
                        break

        self._main_prefix = drives['main'] + os.sep

        def get_card_prefix(c):
            ans = drives.get(c, None)
            if ans is not None:
                ans += os.sep
            return ans

        self._card_a_prefix = get_card_prefix('carda')
        self._card_b_prefix = get_card_prefix('cardb')
Exemplo n.º 3
0
def sanity_check(on_card, files, card_prefixes, free_space):
    if on_card == 'carda' and not card_prefixes[0]:
        raise WrongDestinationError(
            _('The reader has no storage card %s. You may have changed '
              'the default send to device action. Right click on the "Send '
              'to device" button and reset the default action to be '
              '"Send to main memory".') % 'A')
    elif on_card == 'cardb' and not card_prefixes[1]:
        raise WrongDestinationError(
            _('The reader has no storage card %s. You may have changed '
              'the default send to device action. Right click on the "Send '
              'to device" button and reset the default action to be '
              '"Send to main memory".') % 'B')
    elif on_card and on_card not in ('carda', 'cardb'):
        raise DeviceError(_('Selected slot: %s is not supported.') % on_card)

    size = 0
    for f in files:
        size += os.path.getsize(getattr(f, 'name', f))

    if not on_card and size > free_space[0] - 2 * 1024 * 1024:
        raise FreeSpaceError(
            _("There is insufficient free space in main memory"))
    if on_card == 'carda' and size > free_space[1] - 1024 * 1024:
        raise FreeSpaceError(
            _("There is insufficient free space on the storage card"))
    if on_card == 'cardb' and size > free_space[2] - 1024 * 1024:
        raise FreeSpaceError(
            _("There is insufficient free space on the storage card"))
Exemplo n.º 4
0
    def put_file(self,
                 parent,
                 name,
                 stream,
                 size,
                 callback=None,
                 replace=True):
        e = parent.folder_named(name)
        if e is not None:
            raise ValueError(
                'Cannot upload file, %s already has a folder named: %s' %
                (parent.full_path, e.name))
        e = parent.file_named(name)
        if e is not None:
            if not replace:
                raise ValueError('Cannot upload file %s, it already exists' %
                                 (e.full_path, ))
            self.delete_file_or_folder(e)
        sid, pid = parent.storage_id, parent.object_id
        if pid == sid:
            pid = 0xFFFFFFFF

        ans, errs = self.dev.put_file(sid, pid, name, stream, size, callback)
        if ans is None:
            raise DeviceError(
                'Failed to upload file named: %s to %s: %s' %
                (name, parent.full_path, self.format_errorstack(errs)))
        return parent.add_child(ans)
Exemplo n.º 5
0
    def remove_orphaned_records(self, connection, dbpath):
        try:
            cursor = connection.cursor()

            debug_print("Removing Orphaned Collection Records")

            # Purge any collections references that point into the abyss
            query = 'DELETE FROM booktags WHERE book_id NOT IN (SELECT _id FROM books)'
            cursor.execute(query)
            query = 'DELETE FROM booktags WHERE tag_id NOT IN (SELECT _id FROM tags)'
            cursor.execute(query)

            debug_print("Removing Orphaned Book Records")

            cursor.close()
        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)
Exemplo n.º 6
0
    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
Exemplo n.º 7
0
    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(
                f'Could not detect BSD names for {self.name}. Try rebooting.\nOutput from osx_get_usb_drives():\n{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 list(map(int, (g.get('m'), g.get('p'))))

        def cmp_key(x):
            '''
            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]
            disk_num, part_num = nums(x)
            has_part = 1 if part_num > 0 else 0
            return has_part, disk_num, part_num

        matches.sort(key=cmp_key)
        drives = {'main': matches[0]}
        if len(matches) > 1:
            drives['carda'] = matches[1]
        if len(matches) > 2:
            drives['cardb'] = matches[2]

        return drives
Exemplo n.º 8
0
    def open_windows(self):
        from calibre.devices.scanner import drive_is_ok
        from calibre.devices.winusb import get_drive_letters_for_device
        usbdev = self.device_being_opened
        debug = DEBUG or getattr(self, 'do_device_debug', False)
        try:
            dlmap = get_drive_letters_for_device(usbdev, debug=debug)
        except Exception:
            dlmap = {}

        if not dlmap.get('drive_letters'):
            time.sleep(7)
            dlmap = get_drive_letters_for_device(usbdev, debug=debug)

        if debug:
            from pprint import pformat
            prints(f'Drive letters for {usbdev}')
            prints(pformat(dlmap))

        filtered = set()
        for dl in dlmap['drive_letters']:
            pnp_id = dlmap['pnp_id_map'][dl].upper()
            if dl in dlmap['readonly_drives']:
                filtered.add(dl)
                if debug:
                    prints('Ignoring the drive %s as it is readonly' % dl)
            elif self.windows_filter_pnp_id(pnp_id):
                filtered.add(dl)
                if debug:
                    prints(
                        f'Ignoring the drive {dl} because of a PNP filter on {pnp_id}'
                    )
            elif not drive_is_ok(dl, debug=debug):
                filtered.add(dl)
                if debug:
                    prints(
                        'Ignoring the drive %s because failed to get free space for it'
                        % dl)
        dlmap['drive_letters'] = [
            dl for dl in dlmap['drive_letters'] if dl not in filtered
        ]

        if not dlmap['drive_letters']:
            raise DeviceError(
                _('Unable to detect any disk drives for the device: %s. Try rebooting'
                  ) % self.get_gui_name())

        drives = {}

        for drive_letter, which in zip(dlmap['drive_letters'],
                                       'main carda cardb'.split()):
            drives[which] = drive_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)
Exemplo n.º 9
0
    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
Exemplo n.º 10
0
    def open_osx(self):
        from calibre_extensions.usbobserver import get_mounted_filesystems
        bsd_drives = self.osx_bsd_names()
        drives = self.osx_sort_names(bsd_drives.copy())
        mount_map = get_mounted_filesystems()
        # macOS 13 Ventura uses a weird scheme for mounted FAT devices of the
        # form fat://basename_of_bsd_name/basename_of_mountpoint
        # see https://www.mobileread.com/forums/showthread.php?t=347294
        for dev_node in tuple(mount_map):
            if ':' in dev_node and '//' in dev_node:
                val = mount_map[dev_node]
                dev_node = dev_node.split('/')[-2]
                dev_node = f'/dev/{dev_node}'
                if dev_node not in mount_map:
                    mount_map[dev_node] = val
        drives = {k: mount_map.get(v) for k, v in iteritems(drives)}
        if is_debugging():
            print()
            from pprint import pprint
            pprint({
                'bsd_drives': bsd_drives,
                'mount_map': mount_map,
                'drives': drives
            })
        if drives.get('carda') is None and drives.get('cardb') is not None:
            drives['carda'] = drives.pop('cardb')
        if drives.get('main') is None and drives.get('carda') is not None:
            drives['main'] = drives.pop('carda')
        if drives.get('carda') is None and drives.get('cardb') is not None:
            drives['carda'] = drives.pop('cardb')
        if drives.get('main') is None:
            raise DeviceError(
                _('Unable to detect the %s mount point. Try rebooting.') %
                self.__class__.__name__)
        pat = self.OSX_MAIN_MEM_VOL_PAT
        if pat is not None and len(drives) > 1 and 'main' in drives:
            if pat.search(drives['main']) is None:
                main = drives['main']
                for x in ('carda', 'cardb'):
                    if x in drives and pat.search(drives[x]):
                        drives['main'] = drives.pop(x)
                        drives[x] = main
                        break

        self._main_prefix = drives['main'] + os.sep

        def get_card_prefix(c):
            ans = drives.get(c, None)
            if ans is not None:
                ans += os.sep
            return ans

        self._card_a_prefix = get_card_prefix('carda')
        self._card_b_prefix = get_card_prefix('cardb')
Exemplo n.º 11
0
    def open_freebsd(self):
        # 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")
        from .hal import get_hal
        hal = get_hal()
        vols = hal.get_volumes(d)
        if verbose:
            print("FBSD:	", vols)

        ok, mv = hal.mount_volumes(vols)
        if not ok:
            raise DeviceError(_('Unable to mount the device'))
        for k, v in mv.items():
            setattr(self, k, v)
Exemplo n.º 12
0
 def get_mtp_file(self, f, stream=None, callback=None):
     if f.is_folder:
         raise ValueError('%s if a folder'%(f.full_path,))
     set_name = stream is None
     if stream is None:
         stream = SpooledTemporaryFile(5*1024*1024, '_wpd_receive_file.dat')
     ok, errs = self.dev.get_file(f.object_id, stream, callback)
     if not ok:
         raise DeviceError('Failed to get file: %s with errors: %s'%(
             f.full_path, self.format_errorstack(errs)))
     stream.seek(0)
     if set_name:
         stream.name = f.name
     return stream
Exemplo n.º 13
0
    def remove_orphaned_records(self, connection, dbpath):
        from sqlite3 import DatabaseError

        try:
            cursor = connection.cursor()

            debug_print("Removing Orphaned Collection Records")

            # Purge any collections references that point into the abyss
            query = 'DELETE FROM collections WHERE content_id NOT IN (SELECT _id FROM books)'
            cursor.execute(query)
            query = 'DELETE FROM collections WHERE collection_id NOT IN (SELECT _id FROM collection)'
            cursor.execute(query)

            debug_print("Removing Orphaned Book Records")

            # Purge any references to books not in this database
            # Idea is to prevent any spill-over where these wind up applying to some other book
            query = 'DELETE FROM %s WHERE content_id NOT IN (SELECT _id FROM books)'
            cursor.execute(query % 'annotation')
            cursor.execute(query % 'bookmark')
            cursor.execute(query % 'current_position')
            cursor.execute(query % 'freehand')
            cursor.execute(query % 'history')
            cursor.execute(query % 'layout_cache')
            cursor.execute(query % 'preference')

            cursor.close()
        except DatabaseError:
            import traceback
            tb = traceback.format_exc()
            raise DeviceError((
                ('The SONY 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)

        def get_lastrowid(self, cursor):
            # SQLite3 + Python has a fun issue on 32-bit systems with integer overflows.
            # Issue a SQL query instead, getting the value as a string, and then converting to a long python int manually.
            query = 'SELECT last_insert_rowid()'
            cursor.execute(query)
            row = cursor.fetchone()

            return long(row[0])
Exemplo n.º 14
0
 def create_folder(self, parent, name):
     if not parent.is_folder:
         raise ValueError('%s is not a folder' % (parent.full_path, ))
     e = parent.folder_named(name)
     if e is not None:
         return e
     sid, pid = parent.storage_id, parent.object_id
     if pid == sid:
         pid = 0
     ans, errs = self.dev.create_folder(sid, pid, name)
     if ans is None:
         raise DeviceError(
             'Failed to create folder named %s in %s with error: %s' %
             (name, parent.full_path, self.format_errorstack(errs)))
     return parent.add_child(ans)
Exemplo n.º 15
0
    def open_osx(self):
        from calibre_extensions.usbobserver import get_mounted_filesystems
        bsd_drives = self.osx_bsd_names()
        drives = self.osx_sort_names(bsd_drives.copy())
        mount_map = get_mounted_filesystems()
        drives = {k: mount_map.get(v) for k, v in iteritems(drives)}
        if DEBUG:
            print()
            from pprint import pprint
            pprint({
                'bsd_drives': bsd_drives,
                'mount_map': mount_map,
                'drives': drives
            })
        if drives.get('carda') is None and drives.get('cardb') is not None:
            drives['carda'] = drives.pop('cardb')
        if drives.get('main') is None and drives.get('carda') is not None:
            drives['main'] = drives.pop('carda')
        if drives.get('carda') is None and drives.get('cardb') is not None:
            drives['carda'] = drives.pop('cardb')
        if drives.get('main') is None:
            raise DeviceError(
                _('Unable to detect the %s mount point. Try rebooting.') %
                self.__class__.__name__)
        pat = self.OSX_MAIN_MEM_VOL_PAT
        if pat is not None and len(drives) > 1 and 'main' in drives:
            if pat.search(drives['main']) is None:
                main = drives['main']
                for x in ('carda', 'cardb'):
                    if x in drives and pat.search(drives[x]):
                        drives['main'] = drives.pop(x)
                        drives[x] = main
                        break

        self._main_prefix = drives['main'] + os.sep

        def get_card_prefix(c):
            ans = drives.get(c, None)
            if ans is not None:
                ans += os.sep
            return ans

        self._card_a_prefix = get_card_prefix('carda')
        self._card_b_prefix = get_card_prefix('cardb')
Exemplo n.º 16
0
    def _sanity_check(self, on_card, files):
        if on_card == 'carda' and not self._card_a_prefix:
            raise ValueError(_('The reader has no storage card in this slot.'))
        elif on_card == 'cardb' and not self._card_b_prefix:
            raise ValueError(_('The reader has no storage card in this slot.'))
        elif on_card and on_card not in ('carda', 'cardb'):
            raise DeviceError(
                _('Selected slot: %s is not supported.') % on_card)

        if on_card == 'carda':
            path = os.path.join(
                self._card_a_prefix,
                *(self.get_carda_ebook_dir(for_upload=True).split('/')))
        elif on_card == 'cardb':
            path = os.path.join(self._card_b_prefix,
                                *(self.EBOOK_DIR_CARD_B.split('/')))
        else:
            candidates = self.get_main_ebook_dir(for_upload=True)
            if isinstance(candidates, basestring):
                candidates = [candidates]
            candidates = [((os.path.join(self._main_prefix, *(x.split('/'))))
                           if x else self._main_prefix) for x in candidates]
            existing = [x for x in candidates if os.path.exists(x)]
            if not existing:
                existing = candidates[:1]
            path = existing[0]

        def get_size(obj):
            path = getattr(obj, 'name', obj)
            return os.path.getsize(path)

        sizes = [get_size(f) for f in files]
        size = sum(sizes)

        if not on_card and size > self.free_space()[0] - 2 * 1024 * 1024:
            raise FreeSpaceError(
                _("There is insufficient free space in main memory"))
        if on_card == 'carda' and size > self.free_space()[1] - 1024 * 1024:
            raise FreeSpaceError(
                _("There is insufficient free space on the storage card"))
        if on_card == 'cardb' and size > self.free_space()[2] - 1024 * 1024:
            raise FreeSpaceError(
                _("There is insufficient free space on the storage card"))
        return path
Exemplo n.º 17
0
 def get_mtp_file(self, f, stream=None, callback=None):
     if f.is_folder:
         raise ValueError('%s if a folder'%(f.full_path,))
     set_name = stream is None
     if stream is None:
         stream = SpooledTemporaryFile(5*1024*1024, '_wpd_receive_file.dat')
     try:
         try:
             self.dev.get_file(f.object_id, stream, callback)
         except self.wpd.WPDFileBusy:
             time.sleep(2)
             self.dev.get_file(f.object_id, stream, callback)
     except Exception as e:
         raise DeviceError('Failed to fetch the file %s with error: %s'%
                 (f.full_path, as_unicode(e)))
     stream.seek(0)
     if set_name:
         stream.name = f.name
     return stream
Exemplo n.º 18
0
 def delete_file_or_folder(self, obj):
     if obj.deleted:
         return
     if not obj.can_delete:
         raise ValueError('Cannot delete %s as deletion not allowed' %
                          (obj.full_path, ))
     if obj.is_system:
         raise ValueError('Cannot delete %s as it is a system object' %
                          (obj.full_path, ))
     if obj.files or obj.folders:
         raise ValueError('Cannot delete %s as it is not empty' %
                          (obj.full_path, ))
     parent = obj.parent
     ok, errs = self.dev.delete_object(obj.object_id)
     if not ok:
         raise DeviceError('Failed to delete %s with error: %s' %
                           (obj.full_path, self.format_errorstack(errs)))
     parent.remove_child(obj)
     return parent
Exemplo n.º 19
0
 def filesystem_cache(self):
     if self._filesystem_cache is None:
         st = time.time()
         debug('Loading filesystem metadata...')
         from calibre.devices.mtp.filesystem_cache import FilesystemCache
         with self.lock:
             storage, all_items, all_errs = [], [], []
             for sid, capacity in zip(
                 [self._main_id, self._carda_id, self._cardb_id],
                     self.total_space()):
                 if sid is None:
                     continue
                 name = _('Unknown')
                 for x in self.dev.storage_info:
                     if x['id'] == sid:
                         name = x['name']
                         break
                 storage.append({
                     'id': sid,
                     'size': capacity,
                     'is_folder': True,
                     'name': name,
                     'can_delete': False,
                     'is_system': True
                 })
                 self._currently_getting_sid = str(sid)
                 items, errs = self.dev.get_filesystem(
                     sid, partial(self._filesystem_callback, {}))
                 all_items.extend(items), all_errs.extend(errs)
             if not all_items and all_errs:
                 raise DeviceError(
                     'Failed to read filesystem from %s with errors: %s' %
                     (self.current_friendly_name,
                      self.format_errorstack(all_errs)))
             if all_errs:
                 prints('There were some errors while getting the '
                        ' filesystem from %s: %s' %
                        (self.current_friendly_name,
                         self.format_errorstack(all_errs)))
             self._filesystem_cache = FilesystemCache(storage, all_items)
         debug('Filesystem metadata loaded in %g seconds (%d objects)' %
               (time.time() - st, len(self._filesystem_cache)))
     return self._filesystem_cache
Exemplo n.º 20
0
    def filter_read_only_mount_points(self):
        def is_readonly(mp):
            if mp is None:
                return True
            path = os.path.join(mp, 'calibre_readonly_test')
            ro = True
            try:
                with lopen(path, 'wb'):
                    ro = False
            except:
                pass
            else:
                try:
                    os.remove(path)
                except:
                    pass
            if DEBUG and ro:
                print('\nThe mountpoint', mp, 'is readonly, ignoring it')
            return ro

        for mp in ('_main_prefix', '_card_a_prefix', '_card_b_prefix'):
            if is_readonly(getattr(self, mp, None)):
                setattr(self, mp, None)

        if self._main_prefix is None:
            for p in ('_card_a_prefix', '_card_b_prefix'):
                nmp = getattr(self, p, None)
                if nmp is not None:
                    self._main_prefix = nmp
                    setattr(self, p, None)
                    break

        if self._main_prefix is None:
            raise DeviceError(
                _('The main memory of %s is read only. '
                  'This usually happens because of file system errors.') %
                self.__class__.__name__)

        if self._card_a_prefix is None and self._card_b_prefix is not None:
            self._card_a_prefix = self._card_b_prefix
            self._card_b_prefix = None
Exemplo n.º 21
0
    def remove_orphaned_records(self, connection, dbpath):
        from sqlite3 import DatabaseError

        try:
            cursor = connection.cursor()

            debug_print("Removing Orphaned Collection Records")

            # Purge any collections references that point into the abyss
            query = 'DELETE FROM collections WHERE content_id NOT IN (SELECT _id FROM books)'
            cursor.execute(query)
            query = 'DELETE FROM collections WHERE collection_id NOT IN (SELECT _id FROM collection)'
            cursor.execute(query)

            debug_print("Removing Orphaned Book Records")

            # Purge any references to books not in this database
            # Idea is to prevent any spill-over where these wind up applying to some other book
            query = 'DELETE FROM %s WHERE content_id NOT IN (SELECT _id FROM books)'
            cursor.execute(query % 'annotation')
            cursor.execute(query % 'bookmark')
            cursor.execute(query % 'current_position')
            cursor.execute(query % 'freehand')
            cursor.execute(query % 'history')
            cursor.execute(query % 'layout_cache')
            cursor.execute(query % 'preference')

            cursor.close()
        except DatabaseError:
            import traceback
            tb = traceback.format_exc()
            raise DeviceError((
                ('The SONY 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)
Exemplo n.º 22
0
    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...')
Exemplo n.º 23
0
    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:
                    yield from walk(p)

        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

        def getnum(usb_dir):
            def rc(q):
                with open(j(usb_dir, q), 'rb') as f:
                    return raw2num(f.read().decode('utf-8'))

            return rc

        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
                        break
                if usb_dir is None:
                    continue
                ven, prod, bcd = map(getnum(usb_dir),
                                     ('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:
                        with open(sz, 'rb') as szf:
                            exists = int(szf.read().decode('utf-8')) > 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(
                            f'\nIgnoring the node: {node} as could not read size from: {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])
Exemplo n.º 24
0
    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'))
Exemplo n.º 25
0
    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)
Exemplo n.º 26
0
    def read_device_collections(self, connection, source_id, dbpath):
        from sqlite3 import DatabaseError

        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, title FROM collection'
            cursor.execute(query)
        except DatabaseError:
            import traceback
            tb = traceback.format_exc()
            raise DeviceError((('The SONY 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 collection SET _id = ? WHERE title = ?'
                    t = (db_collections[collection], collection, )
                    cursor.execute(query, t)

                    # Fix any references in existing collections
                    query = 'UPDATE collections SET collection_id = ? WHERE collection_id = ?'
                    t = (db_collections[collection], collectionId,)
                    cursor.execute(query, t)

            self.set_database_sequence_id(connection, 'collection', 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 collections'
        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 collections SET _id = ? WHERE _id = ?'
                    t = (sequence_max, pairId,)
                    cursor.execute(query, t)
                    sequence_max = sequence_max + 1

            self.set_database_sequence_id(connection, 'collections', sequence_max)
            debug_print("Collections Sequence Max: %d, Source Id: %d"%(sequence_max,source_id))

        cursor.close()
        return db_collections
Exemplo n.º 27
0
    def read_device_books(self, connection, source_id, dbpath):
        from sqlite3 import DatabaseError

        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 file_path, _id FROM books'
            cursor.execute(query)
        except DatabaseError:
            import traceback
            tb = traceback.format_exc()
            raise DeviceError((('The SONY 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 file_path = ?'
                    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 collections SET content_id = ? WHERE content_id = ?'
                    cursor.execute(query, t)
                    query = 'UPDATE annotation SET content_id = ? WHERE content_id = ?'
                    cursor.execute(query, t)
                    query = 'UPDATE bookmark SET content_id = ? WHERE content_id = ?'
                    cursor.execute(query, t)
                    query = 'UPDATE current_position SET content_id = ? WHERE content_id = ?'
                    cursor.execute(query, t)
                    query = 'UPDATE deleted_markups SET content_id = ? WHERE content_id = ?'
                    cursor.execute(query, t)
                    query = 'UPDATE dic_histories SET content_id = ? WHERE content_id = ?'
                    cursor.execute(query, t)
                    query = 'UPDATE freehand SET content_id = ? WHERE content_id = ?'
                    cursor.execute(query, t)
                    query = 'UPDATE history SET content_id = ? WHERE content_id = ?'
                    cursor.execute(query, t)
                    query = 'UPDATE layout_cache SET content_id = ? WHERE content_id = ?'
                    cursor.execute(query, t)
                    query = 'UPDATE preference SET content_id = ? WHERE content_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
Exemplo n.º 28
0
                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: