From 9d697e814637577ace962e903ef6267384ffab32 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 25 Aug 2012 09:34:12 +0530 Subject: [PATCH] Remove legacy code --- .../devices/{prs500/cli/main.py => cli.py} | 8 +- src/calibre/devices/libusb.py | 368 ------- src/calibre/devices/prs500/__init__.py | 6 - src/calibre/devices/prs500/books.py | 385 ------- src/calibre/devices/prs500/cli/__init__.py | 9 - src/calibre/devices/prs500/driver.py | 989 ------------------ src/calibre/devices/prs500/prstypes.py | 861 --------------- src/calibre/ebooks/lrf/meta.py | 30 +- src/calibre/linux.py | 12 +- src/calibre/trac/setup.py | 21 - 10 files changed, 39 insertions(+), 2650 deletions(-) rename src/calibre/devices/{prs500/cli/main.py => cli.py} (98%) delete mode 100644 src/calibre/devices/libusb.py delete mode 100644 src/calibre/devices/prs500/__init__.py delete mode 100644 src/calibre/devices/prs500/books.py delete mode 100644 src/calibre/devices/prs500/cli/__init__.py delete mode 100644 src/calibre/devices/prs500/driver.py delete mode 100755 src/calibre/devices/prs500/prstypes.py delete mode 100644 src/calibre/trac/setup.py diff --git a/src/calibre/devices/prs500/cli/main.py b/src/calibre/devices/cli.py similarity index 98% rename from src/calibre/devices/prs500/cli/main.py rename to src/calibre/devices/cli.py index 16a9ab7b0a..95181bf639 100755 --- a/src/calibre/devices/prs500/cli/main.py +++ b/src/calibre/devices/cli.py @@ -1,7 +1,7 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' """ -Provides a command-line and optional graphical interface to the SONY Reader PRS-500. +Provides a command-line interface to ebook devices. For usage information run the script. """ @@ -275,7 +275,7 @@ def main(): elif command == "cp": usage="usage: %prog cp [options] source destination\nCopy files to/from the device\n\n"+\ "One of source or destination must be a path on the device. \n\nDevice paths have the form\n"+\ - "prs500:mountpoint/my/path\n"+\ + "dev:mountpoint/my/path\n"+\ "where mountpoint is one of / or card:/\n\n"+\ "source must point to a file for which you have read permissions\n"+\ "destination must point to a file or directory for which you have write permissions" @@ -286,7 +286,7 @@ def main(): if len(args) != 2: parser.print_help() return 1 - if args[0].startswith("prs500:"): + if args[0].startswith("dev:"): outfile = args[1] path = args[0][7:] if path.endswith("/"): path = path[:-1] @@ -300,7 +300,7 @@ def main(): return 1 dev.get_file(path, outfile) outfile.close() - elif args[1].startswith("prs500:"): + elif args[1].startswith("dev:"): try: infile = open(args[0], "rb") except IOError as e: diff --git a/src/calibre/devices/libusb.py b/src/calibre/devices/libusb.py deleted file mode 100644 index 016a6b18aa..0000000000 --- a/src/calibre/devices/libusb.py +++ /dev/null @@ -1,368 +0,0 @@ -__license__ = 'GPL v3' -__copyright__ = '2008, Kovid Goyal ' -""" -This module provides a thin ctypes based wrapper around libusb. -""" - -from ctypes import cdll, POINTER, byref, pointer, Structure as _Structure, \ - c_ubyte, c_ushort, c_int, c_char, c_void_p, c_byte, c_uint -from errno import EBUSY, ENOMEM - -from calibre import iswindows, isosx, isbsd, load_library - -_libusb_name = 'libusb' -PATH_MAX = 511 if iswindows else 1024 if (isosx or isbsd) else 4096 -if iswindows: - class Structure(_Structure): - _pack_ = 1 - _libusb_name = 'libusb0' -else: - Structure = _Structure - -try: - try: - _libusb = load_library(_libusb_name, cdll) - except OSError: - _libusb = cdll.LoadLibrary('libusb-0.1.so.4') - has_library = True -except: - _libusb = None - has_library = False - -class DeviceDescriptor(Structure): - _fields_ = [\ - ('Length', c_ubyte), \ - ('DescriptorType', c_ubyte), \ - ('bcdUSB', c_ushort), \ - ('DeviceClass', c_ubyte), \ - ('DeviceSubClass', c_ubyte), \ - ('DeviceProtocol', c_ubyte), \ - ('MaxPacketSize0', c_ubyte), \ - ('idVendor', c_ushort), \ - ('idProduct', c_ushort), \ - ('bcdDevice', c_ushort), \ - ('Manufacturer', c_ubyte), \ - ('Product', c_ubyte), \ - ('SerialNumber', c_ubyte), \ - ('NumConfigurations', c_ubyte) \ - ] - -class EndpointDescriptor(Structure): - _fields_ = [\ - ('Length', c_ubyte), \ - ('DescriptorType', c_ubyte), \ - ('EndpointAddress', c_ubyte), \ - ('Attributes', c_ubyte), \ - ('MaxPacketSize', c_ushort), \ - ('Interval', c_ubyte), \ - ('Refresh', c_ubyte), \ - ('SynchAddress', c_ubyte), \ - ('extra', POINTER(c_char)), \ - ('extralen', c_int)\ - ] - -class InterfaceDescriptor(Structure): - _fields_ = [\ - ('Length', c_ubyte), \ - ('DescriptorType', c_ubyte), \ - ('InterfaceNumber', c_ubyte), \ - ('AlternateSetting', c_ubyte), \ - ('NumEndpoints', c_ubyte), \ - ('InterfaceClass', c_ubyte), \ - ('InterfaceSubClass', c_ubyte), \ - ('InterfaceProtocol', c_ubyte), \ - ('Interface', c_ubyte), \ - ('endpoint', POINTER(EndpointDescriptor)), \ - ('extra', POINTER(c_char)), \ - ('extralen', c_int)\ - ] - -class Interface(Structure): - _fields_ = [\ - ('altsetting', POINTER(InterfaceDescriptor)), \ - ('num_altsetting', c_int)\ - ] - -class ConfigDescriptor(Structure): - _fields_ = [\ - ('Length', c_ubyte), \ - ('DescriptorType', c_ubyte), \ - ('TotalLength', c_ushort), \ - ('NumInterfaces', c_ubyte), \ - ('Value', c_ubyte), \ - ('Configuration', c_ubyte), \ - ('Attributes', c_ubyte), \ - ('MaxPower', c_ubyte), \ - ('interface', POINTER(Interface)), \ - ('extra', POINTER(c_ubyte)), \ - ('extralen', c_int) \ - ] - - def __str__(self): - ans = "" - for field in self._fields_: - ans += field[0] + ": " + str(eval('self.'+field[0])) + '\n' - return ans.strip() - - - -class Error(Exception): - pass - -class Device(Structure): - - def open(self): - """ Open device for use. Return a DeviceHandle. """ - handle = _libusb.usb_open(byref(self)) - if not handle: - raise Error("Cannot open device") - return handle.contents - - @dynamic_property - def configurations(self): - doc = """ List of device configurations. See L{ConfigDescriptor} """ - def fget(self): - ans = [] - for config in range(self.device_descriptor.NumConfigurations): - ans.append(self.config_descriptor[config]) - return tuple(ans) - return property(doc=doc, fget=fget) - -class Bus(Structure): - @dynamic_property - def device_list(self): - doc = \ - """ - Flat list of devices on this bus. - Note: children are not explored - TODO: Check if exploring children is neccessary (e.g. with an external hub) - """ - def fget(self): - if _libusb is None: - return [] - if _libusb.usb_find_devices() < 0: - raise Error('Unable to search for USB devices') - ndev = self.devices - ans = [] - while ndev: - dev = ndev.contents - ans.append(dev) - ndev = dev.next - return ans - return property(doc=doc, fget=fget) - -class DeviceHandle(Structure): - _fields_ = [\ - ('fd', c_int), \ - ('bus', POINTER(Bus)), \ - ('device', POINTER(Device)), \ - ('config', c_int), \ - ('interface', c_int), \ - ('altsetting', c_int), \ - ('impl_info', c_void_p) - ] - - def close(self): - """ Close this DeviceHandle """ - _libusb.usb_close(byref(self)) - - def set_configuration(self, config): - """ - Set device configuration. This has to be called on windows before - trying to claim an interface. - @param config: A L{ConfigDescriptor} or a integer (the ConfigurationValue) - """ - try: - num = config.Value - except AttributeError: - num = config - ret = _libusb.usb_set_configuration(byref(self), num) - if ret < 0: - raise Error('Failed to set device configuration to: ' + str(num) + \ - '. Error code: ' + str(ret)) - - def claim_interface(self, num): - """ - Claim interface C{num} on device. - Must be called before doing anything witht the device. - """ - ret = _libusb.usb_claim_interface(byref(self), num) - - if -ret == ENOMEM: - raise Error("Insufficient memory to claim interface") - elif -ret == EBUSY: - raise Error('Device busy') - elif ret < 0: - raise Error('Unknown error occurred while trying to claim USB'\ - ' interface: ' + str(ret)) - - def control_msg(self, rtype, request, bytes, value=0, index=0, timeout=100): - """ - Perform a control request to the default control pipe on the device. - @param rtype: specifies the direction of data flow, the type - of request, and the recipient. - @param request: specifies the request. - @param bytes: if the transfer is a write transfer, buffer is a sequence - with the transfer data, otherwise, buffer is the number of - bytes to read. - @param value: specific information to pass to the device. - @param index: specific information to pass to the device. - """ - size = 0 - try: - size = len(bytes) - except TypeError: - size = bytes - ArrayType = c_byte * size - _libusb.usb_control_msg.argtypes = [POINTER(DeviceHandle), c_int, \ - c_int, c_int, c_int, \ - POINTER(ArrayType), \ - c_int, c_int] - arr = ArrayType() - rsize = _libusb.usb_control_msg(byref(self), rtype, request, \ - value, index, byref(arr), \ - size, timeout) - if rsize < size: - raise Error('Could not read ' + str(size) + ' bytes on the '\ - 'control bus. Read: ' + str(rsize) + ' bytes.') - return arr - else: - ArrayType = c_byte * size - _libusb.usb_control_msg.argtypes = [POINTER(DeviceHandle), c_int, \ - c_int, c_int, c_int, \ - POINTER(ArrayType), \ - c_int, c_int] - arr = ArrayType(*bytes) - return _libusb.usb_control_msg(byref(self), rtype, request, \ - value, index, byref(arr), \ - size, timeout) - - def bulk_read(self, endpoint, size, timeout=100): - """ - Read C{size} bytes via a bulk transfer from the device. - """ - ArrayType = c_byte * size - arr = ArrayType() - _libusb.usb_bulk_read.argtypes = [POINTER(DeviceHandle), c_int, \ - POINTER(ArrayType), c_int, c_int - ] - rsize = _libusb.usb_bulk_read(byref(self), endpoint, byref(arr), \ - size, timeout) - if rsize < 0: - raise Error('Could not read ' + str(size) + ' bytes on the '\ - 'bulk bus. Error code: ' + str(rsize)) - if rsize == 0: - raise Error('Device sent zero bytes') - if rsize < size: - arr = arr[:rsize] - return arr - - def bulk_write(self, endpoint, bytes, timeout=100): - """ - Send C{bytes} to device via a bulk transfer. - """ - size = len(bytes) - ArrayType = c_byte * size - arr = ArrayType(*bytes) - _libusb.usb_bulk_write.argtypes = [POINTER(DeviceHandle), c_int, \ - POINTER(ArrayType), c_int, c_int - ] - _libusb.usb_bulk_write(byref(self), endpoint, byref(arr), size, timeout) - - def release_interface(self, num): - ret = _libusb.usb_release_interface(pointer(self), num) - if ret < 0: - raise Error('Unknown error occurred while trying to release USB'\ - ' interface: ' + str(ret)) - - def reset(self): - ret = _libusb.usb_reset(pointer(self)) - if ret < 0: - raise Error('Unknown error occurred while trying to reset '\ - 'USB device ' + str(ret)) - - -Bus._fields_ = [ \ - ('next', POINTER(Bus)), \ - ('previous', POINTER(Bus)), \ - ('dirname', c_char * (PATH_MAX+1)), \ - ('devices', POINTER(Device)), \ - ('location', c_uint), \ - ('root_dev', POINTER(Device))\ - ] - -Device._fields_ = [ \ - ('next', POINTER(Device)), \ - ('previous', POINTER(Device)), \ - ('filename', c_char * (PATH_MAX+1)), \ - ('bus', POINTER(Bus)), \ - ('device_descriptor', DeviceDescriptor), \ - ('config_descriptor', POINTER(ConfigDescriptor)), \ - ('dev', c_void_p), \ - ('devnum', c_ubyte), \ - ('num_children', c_ubyte), \ - ('children', POINTER(POINTER(Device))) - ] - -if _libusb is not None: - try: - _libusb.usb_get_busses.restype = POINTER(Bus) - _libusb.usb_open.restype = POINTER(DeviceHandle) - _libusb.usb_open.argtypes = [POINTER(Device)] - _libusb.usb_close.argtypes = [POINTER(DeviceHandle)] - _libusb.usb_claim_interface.argtypes = [POINTER(DeviceHandle), c_int] - _libusb.usb_claim_interface.restype = c_int - _libusb.usb_release_interface.argtypes = [POINTER(DeviceHandle), c_int] - _libusb.usb_release_interface.restype = c_int - _libusb.usb_reset.argtypes = [POINTER(DeviceHandle)] - _libusb.usb_reset.restype = c_int - _libusb.usb_control_msg.restype = c_int - _libusb.usb_bulk_read.restype = c_int - _libusb.usb_bulk_write.restype = c_int - _libusb.usb_set_configuration.argtypes = [POINTER(DeviceHandle), c_int] - _libusb.usb_set_configuration.restype = c_int - _libusb.usb_init() - except: - _libusb = None - - - -def busses(): - """ Get list of USB busses present on system """ - if _libusb is None: - raise Error('Could not find libusb.') - if _libusb.usb_find_busses() < 0: - raise Error('Unable to search for USB busses') - if _libusb.usb_find_devices() < 0: - raise Error('Unable to search for USB devices') - ans = [] - nbus = _libusb.usb_get_busses() - while nbus: - bus = nbus.contents - ans.append(bus) - nbus = bus.next - return ans - - -def get_device_by_id(idVendor, idProduct): - """ Return a L{Device} by vendor and prduct ids """ - buslist = busses() - for bus in buslist: - devices = bus.device_list - for dev in devices: - if dev.device_descriptor.idVendor == idVendor and \ - dev.device_descriptor.idProduct == idProduct: - return dev - -def has_library(): - return _libusb is not None - -def get_devices(): - buslist = busses() - ans = [] - for bus in buslist: - devices = bus.device_list - for dev in devices: - device = (dev.device_descriptor.idVendor, dev.device_descriptor.idProduct, dev.device_descriptor.bcdDevice) - ans.append(device) - return ans diff --git a/src/calibre/devices/prs500/__init__.py b/src/calibre/devices/prs500/__init__.py deleted file mode 100644 index d9f4403944..0000000000 --- a/src/calibre/devices/prs500/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -__license__ = 'GPL v3' -__copyright__ = '2008, Kovid Goyal ' - -''' -Device driver for the Sony Reader PRS 500 -''' \ No newline at end of file diff --git a/src/calibre/devices/prs500/books.py b/src/calibre/devices/prs500/books.py deleted file mode 100644 index 91fcb3255f..0000000000 --- a/src/calibre/devices/prs500/books.py +++ /dev/null @@ -1,385 +0,0 @@ -__license__ = 'GPL v3' -__copyright__ = '2008, Kovid Goyal ' -""" -This module contains the logic for dealing with XML book lists found -in the reader cache. -""" -import xml.dom.minidom as dom -from base64 import b64decode as decode -from base64 import b64encode as encode -import re - -from calibre.devices.interface import BookList as _BookList -from calibre.devices import strftime, strptime - -MIME_MAP = { \ - "lrf":"application/x-sony-bbeb", \ - 'lrx':'application/x-sony-bbeb', \ - "rtf":"application/rtf", \ - "pdf":"application/pdf", \ - "txt":"text/plain" \ - } - -def sortable_title(title): - return re.sub('^\s*A\s+|^\s*The\s+|^\s*An\s+', '', title).rstrip() - -class book_metadata_field(object): - """ Represents metadata stored as an attribute """ - def __init__(self, attr, formatter=None, setter=None): - self.attr = attr - self.formatter = formatter - self.setter = setter - - def __get__(self, obj, typ=None): - """ Return a string. String may be empty if self.attr is absent """ - return self.formatter(obj.elem.getAttribute(self.attr)) if \ - self.formatter else obj.elem.getAttribute(self.attr).strip() - - def __set__(self, obj, val): - """ Set the attribute """ - val = self.setter(val) if self.setter else val - if not isinstance(val, unicode): - val = unicode(val, 'utf8', 'replace') - obj.elem.setAttribute(self.attr, val) - -class Book(object): - """ Provides a view onto the XML element that represents a book """ - - title = book_metadata_field("title") - authors = book_metadata_field("author", \ - formatter=lambda x: x if x and x.strip() else "Unknown") - mime = book_metadata_field("mime") - rpath = book_metadata_field("path") - id = book_metadata_field("id", formatter=int) - sourceid = book_metadata_field("sourceid", formatter=int) - size = book_metadata_field("size", formatter=int) - # When setting this attribute you must use an epoch - datetime = book_metadata_field("date", formatter=strptime, setter=strftime) - @dynamic_property - def title_sorter(self): - doc = '''String to sort the title. If absent, title is returned''' - def fget(self): - src = self.elem.getAttribute('titleSorter').strip() - if not src: - src = self.title - return src - def fset(self, val): - self.elem.setAttribute('titleSorter', sortable_title(unicode(val))) - return property(doc=doc, fget=fget, fset=fset) - - @dynamic_property - def thumbnail(self): - doc = \ - """ - The thumbnail. Should be a height 68 image. - Setting is not supported. - """ - def fget(self): - th = self.elem.getElementsByTagName(self.prefix + "thumbnail") - if len(th): - for n in th[0].childNodes: - if n.nodeType == n.ELEMENT_NODE: - th = n - break - rc = "" - for node in th.childNodes: - if node.nodeType == node.TEXT_NODE: - rc += node.data - return decode(rc) - return property(fget=fget, doc=doc) - - @dynamic_property - def path(self): - doc = """ Absolute path to book on device. Setting not supported. """ - def fget(self): - return self.root + self.rpath - return property(fget=fget, doc=doc) - - @dynamic_property - def db_id(self): - doc = '''The database id in the application database that this file corresponds to''' - def fget(self): - match = re.search(r'_(\d+)$', self.rpath.rpartition('.')[0]) - if match: - return int(match.group(1)) - return property(fget=fget, doc=doc) - - def __init__(self, node, tags=[], prefix="", root="/Data/media/"): - self.elem = node - self.prefix = prefix - self.root = root - self.tags = tags - - def __str__(self): - """ Return a utf-8 encoded string with title author and path information """ - return self.title.encode('utf-8') + " by " + \ - self.authors.encode('utf-8') + " at " + self.path.encode('utf-8') - - -def fix_ids(media, cache, *args): - ''' - Adjust ids in cache to correspond with media. - ''' - media.purge_empty_playlists() - media.reorder_playlists() - if cache.root: - sourceid = media.max_id() - cid = sourceid + 1 - for child in cache.root.childNodes: - if child.nodeType == child.ELEMENT_NODE and child.hasAttribute("sourceid"): - child.setAttribute("sourceid", str(sourceid)) - child.setAttribute("id", str(cid)) - cid += 1 - media.set_next_id(str(cid)) - - -class BookList(_BookList): - """ - A list of L{Book}s. Created from an XML file. Can write list - to an XML file. - """ - __getslice__ = None - __setslice__ = None - - def __init__(self, root="/Data/media/", sfile=None): - _BookList.__init__(self) - self.tag_order = {} - self.root = self.document = self.proot = None - if sfile: - sfile.seek(0) - src = sfile.read() - try: - src = src.decode('utf8') - except UnicodeDecodeError: - try: - src = src.decode('latin1') - except UnicodeDecodeError: - src = src.decode('cp1252') - src = src.replace(' max: - max = nid - return max - - def book_by_path(self, path): - for child in self.root.childNodes: - if child.nodeType == child.ELEMENT_NODE and child.hasAttribute("path"): - if path == child.getAttribute('path'): - return child - return None - - def add_book(self, mi, name, size, ctime): - """ Add a node into DOM tree representing a book """ - book = self.book_by_path(name) - if book is not None: - self.remove_book(name) - node = self.document.createElement(self.prefix + "text") - mime = MIME_MAP[name[name.rfind(".")+1:].lower()] - cid = self.max_id()+1 - sourceid = str(self[0].sourceid) if len(self) else "1" - attrs = { - "title" : mi.title, - 'titleSorter' : sortable_title(mi.title), - "author" : mi.format_authors() if mi.format_authors() else _('Unknown'), - "page":"0", "part":"0", "scale":"0", \ - "sourceid":sourceid, "id":str(cid), "date":"", \ - "mime":mime, "path":name, "size":str(size) - } - for attr in attrs.keys(): - node.setAttributeNode(self.document.createAttribute(attr)) - node.setAttribute(attr, attrs[attr]) - try: - w, h, data = mi.thumbnail - except: - w, h, data = None, None, None - - if data: - th = self.document.createElement(self.prefix + "thumbnail") - th.setAttribute("width", str(w)) - th.setAttribute("height", str(h)) - jpeg = self.document.createElement(self.prefix + "jpeg") - jpeg.appendChild(self.document.createTextNode(encode(data))) - th.appendChild(jpeg) - node.appendChild(th) - self.root.appendChild(node) - book = Book(node, root=self.proot, prefix=self.prefix) - book.datetime = ctime - self.append(book) - self.set_next_id(cid+1) - tags = [] - if mi.tags: - tags.extend(mi.tags) - if mi.series: - tags.append(mi.series) - if self.prefix and tags: # Playlists only supportted in main memory - if hasattr(mi, 'tag_order'): - self.tag_order.update(mi.tag_order) - self.set_tags(book, tags) - - def playlist_by_title(self, title): - for pl in self.playlists(): - if pl.getAttribute('title').lower() == title.lower(): - return pl - - def add_playlist(self, title): - cid = self.max_id()+1 - pl = self.document.createElement(self.prefix+'playlist') - pl.setAttribute('sourceid', '0') - pl.setAttribute('id', str(cid)) - pl.setAttribute('title', title) - for child in self.root.childNodes: - try: - if child.getAttribute('id') == '1': - self.root.insertBefore(pl, child) - self.set_next_id(cid+1) - break - except AttributeError: - continue - return pl - - - def remove_from_playlists(self, id): - for pli in self.playlist_items(): - if pli.getAttribute('id') == str(id): - pli.parentNode.removeChild(pli) - pli.unlink() - - def set_tags(self, book, tags): - book.tags = tags - self.set_playlists(book.id, tags) - - def set_playlists(self, id, collections): - self.remove_from_playlists(id) - for collection in set(collections): - coll = self.playlist_by_title(collection) - if not coll: - coll = self.add_playlist(collection) - item = self.document.createElement(self.prefix+'item') - item.setAttribute('id', str(id)) - coll.appendChild(item) - - def get_playlists(self, id): - ans = [] - for pl in self.playlists(): - for item in pl.getElementsByTagName(self.prefix+'item'): - if item.getAttribute('id') == str(id): - ans.append(pl) - continue - return ans - - def book_by_id(self, id): - for book in self: - if str(book.id) == str(id): - return book - - def reorder_playlists(self): - for title in self.tag_order.keys(): - pl = self.playlist_by_title(title) - if not pl: - continue - db_ids = [i.getAttribute('id') for i in pl.childNodes if hasattr(i, 'getAttribute')] - pl_book_ids = [self.book_by_id(i.getAttribute('id')).db_id for i in pl.childNodes if hasattr(i, 'getAttribute')] - map = {} - for i, j in zip(pl_book_ids, db_ids): - map[i] = j - pl_book_ids = [i for i in pl_book_ids if i is not None] - ordered_ids = [i for i in self.tag_order[title] if i in pl_book_ids] - - if len(ordered_ids) < len(pl.childNodes): - continue - children = [i for i in pl.childNodes if hasattr(i, 'getAttribute')] - for child in children: - pl.removeChild(child) - child.unlink() - for id in ordered_ids: - item = self.document.createElement(self.prefix+'item') - item.setAttribute('id', str(map[id])) - pl.appendChild(item) - - def write(self, stream): - """ Write XML representation of DOM tree to C{stream} """ - stream.write(self.document.toxml('utf-8')) diff --git a/src/calibre/devices/prs500/cli/__init__.py b/src/calibre/devices/prs500/cli/__init__.py deleted file mode 100644 index 6b376c2ad3..0000000000 --- a/src/calibre/devices/prs500/cli/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -__license__ = 'GPL v3' -__copyright__ = '2008, Kovid Goyal ' -""" -Provides a command-line interface to the SONY Reader PRS-500. - -For usage information run the script. -""" -__docformat__ = "epytext" -__author__ = "Kovid Goyal " diff --git a/src/calibre/devices/prs500/driver.py b/src/calibre/devices/prs500/driver.py deleted file mode 100644 index 28545b2d86..0000000000 --- a/src/calibre/devices/prs500/driver.py +++ /dev/null @@ -1,989 +0,0 @@ -__license__ = 'GPL v3' -__copyright__ = '2008, Kovid Goyal ' - -### End point description for PRS-500 procductId=667 -### Endpoint Descriptor: -### bLength 7 -### bDescriptorType 5 -### bEndpointAddress 0x81 EP 1 IN -### bmAttributes 2 -### Transfer Type Bulk -### Synch Type None -### Usage Type Data -### wMaxPacketSize 0x0040 1x 64 bytes -### bInterval 0 -### Endpoint Descriptor: -### bLength 7 -### bDescriptorType 5 -### bEndpointAddress 0x02 EP 2 OUT -### bmAttributes 2 -### Transfer Type Bulk -### Synch Type None -### Usage Type Data -### wMaxPacketSize 0x0040 1x 64 bytes -### bInterval 0 -### -### -### Endpoint 0x81 is device->host and endpoint 0x02 is host->device. -### You can establish Stream pipes to/from these endpoints for Bulk transfers. -### Has two configurations 1 is the USB charging config 2 is the self-powered -### config. I think config management is automatic. Endpoints are the same -""" -Contains the logic for communication with the device (a SONY PRS-500). - -The public interface of class L{PRS500} defines the -methods for performing various tasks. -""" -import sys, os -from tempfile import TemporaryFile -from array import array -from functools import wraps -from StringIO import StringIO -from threading import RLock - -from calibre.devices.interface import DevicePlugin -from calibre.devices.libusb import Error as USBError -from calibre.devices.libusb import get_device_by_id -from calibre.devices.prs500.prstypes import * -from calibre.devices.errors import * -from calibre.devices.prs500.books import BookList, fix_ids -from calibre import __author__, __appname__ -from calibre.devices.usbms.deviceconfig import DeviceConfig - -# Protocol versions this driver has been tested with -KNOWN_USB_PROTOCOL_VERSIONS = [0x3030303030303130L] - -lock = RLock() - -class File(object): - """ - Wrapper that allows easy access to all information about files/directories - """ - def __init__(self, _file): - self.is_dir = _file[1].is_dir #: True if self is a directory - self.is_readonly = _file[1].is_readonly #: True if self is readonly - self.size = _file[1].file_size #: Size in bytes of self - self.ctime = _file[1].ctime #: Creation time of self as a epoch - self.wtime = _file[1].wtime #: Creation time of self as an epoch - path = _file[0] - if path.endswith("/"): - path = path[:-1] - self.path = path #: Path to self - self.name = path[path.rfind("/")+1:].rstrip() #: Name of self - - def __repr__(self): - """ Return path to self """ - return "File:" + self.path - - def __str__(self): - return self.name - - -class PRS500(DeviceConfig, DevicePlugin): - - """ - Implements the backend for communication with the SONY Reader. - Each method decorated by C{safe} performs a task. - """ - name = 'PRS-500 Device Interface' - description = _('Communicate with the Sony PRS-500 eBook reader.') - author = _('Kovid Goyal') - supported_platforms = ['windows', 'osx', 'linux'] - log_packets = False - - VENDOR_ID = 0x054c #: SONY Vendor Id - PRODUCT_ID = 0x029b #: Product Id for the PRS-500 - BCD = [0x100] - PRODUCT_NAME = 'PRS-500' - gui_name = PRODUCT_NAME - VENDOR_NAME = 'SONY' - INTERFACE_ID = 0 #: The interface we use to talk to the device - BULK_IN_EP = 0x81 #: Endpoint for Bulk reads - BULK_OUT_EP = 0x02 #: Endpoint for Bulk writes - # Location of media.xml file on device - MEDIA_XML = "/Data/database/cache/media.xml" - # Location of cache.xml on storage card in device - CACHE_XML = "/Sony Reader/database/cache.xml" - # Ordered list of supported formats - FORMATS = ["lrf", "lrx", "rtf", "pdf", "txt"] - # Height for thumbnails of books/images on the device - THUMBNAIL_HEIGHT = 68 - # Directory on card to which books are copied - CARD_PATH_PREFIX = __appname__ - _packet_number = 0 #: Keep track of the packet number for packet tracing - - SUPPORTS_SUB_DIRS = False - MUST_READ_METADATA = True - - def log_packet(self, packet, header, stream=sys.stderr): - """ - Log C{packet} to stream C{stream}. - Header should be a small word describing the type of packet. - """ - self._packet_number += 1 - print >> stream, str(self._packet_number), header, "Type:", \ - packet.__class__.__name__ - print >> stream, packet - print >> stream, "--" - - @classmethod - def validate_response(cls, res, _type=0x00, number=0x00): - """ - Raise a ProtocolError if the type and number of C{res} - is not the same as C{type} and C{number}. - """ - if _type != res.type or number != res.rnumber: - raise ProtocolError("Inavlid response.\ntype: expected=" + \ - hex(_type)+" actual=" + hex(res.type) + \ - "\nrnumber: expected=" + hex(number) + \ - " actual="+hex(res.rnumber)) - - @classmethod - def signature(cls): - """ Return a two element tuple (vendor id, product id) """ - return (cls.VENDOR_ID, cls.PRODUCT_ID ) - - def safe(func): - """ - Decorator that wraps a call to C{func} to ensure that - exceptions are handled correctly. It also calls L{open} to claim - the interface and initialize the Reader if needed. - - As a convenience, C{safe} automatically sends the a - L{EndSession} after calling func, unless func has - a keyword argument named C{end_session} set to C{False}. - - An L{ArgumentError} will cause the L{EndSession} command to - be sent to the device, unless end_session is set to C{False}. - An L{usb.USBError} will cause the library to release control of the - USB interface via a call to L{close}. - """ - @wraps(func) - def run_session(*args, **kwargs): - with lock: - dev = args[0] - res = None - try: - if not hasattr(dev, 'in_session'): - dev.reset() - if not dev.handle: - dev.open() - if not getattr(dev, 'in_session', False): - dev.send_validated_command(BeginEndSession(end=False)) - dev.in_session = True - res = func(*args, **kwargs) - except ArgumentError: - if not kwargs.has_key("end_session") or kwargs["end_session"]: - dev.send_validated_command(BeginEndSession(end=True)) - dev.in_session = False - raise - except USBError as err: - if "No such device" in str(err): - raise DeviceError() - elif "Connection timed out" in str(err): - dev.close() - raise TimeoutError(func.__name__) - elif "Protocol error" in str(err): - dev.close() - raise ProtocolError("There was an unknown error in the"+\ - " protocol. Contact " + __author__) - dev.close() - raise - if not kwargs.has_key("end_session") or kwargs["end_session"]: - dev.send_validated_command(BeginEndSession(end=True)) - dev.in_session = False - return res - - return run_session - - def reset(self, key='-1', log_packets=False, report_progress=None, - detected_device=None) : - """ - @param key: The key to unlock the device - @param log_packets: If true the packet stream to/from the device is logged - @param report_progress: Function that is called with a % progress - (number between 0 and 100) for various tasks - If it is called with -1 that means that the - task does not have any progress information - """ - with lock: - self.device = get_device_by_id(self.VENDOR_ID, self.PRODUCT_ID) - # Handle that is used to communicate with device. Setup in L{open} - self.handle = None - self.in_session = False - self.log_packets = log_packets - self.report_progress = report_progress - if len(key) > 8: - key = key[:8] - elif len(key) < 8: - key += ''.join(['\0' for i in xrange(8 - len(key))]) - self.key = key - - def reconnect(self): - """ Only recreates the device node and deleted the connection handle """ - self.device = get_device_by_id(self.VENDOR_ID, self.PRODUCT_ID) - self.handle = None - - @classmethod - def is_connected(cls, helper=None): - """ - This method checks to see whether the device is physically connected. - It does not return any information about the validity of the - software connection. You may need to call L{reconnect} if you keep - getting L{DeviceError}. - """ - try: - return get_device_by_id(cls.VENDOR_ID, cls.PRODUCT_ID) != None - except USBError: - return False - - def set_progress_reporter(self, report_progress): - self.report_progress = report_progress - - def open(self, connected_device, library_uuid) : - """ - Claim an interface on the device for communication. - Requires write privileges to the device file. - Also initialize the device. - See the source code for the sequence of initialization commands. - """ - with lock: - if not hasattr(self, 'key'): - self.reset() - self.device = get_device_by_id(self.VENDOR_ID, self.PRODUCT_ID) - if not self.device: - raise DeviceError() - configs = self.device.configurations - try: - self.handle = self.device.open() - config = configs[0] - try: - self.handle.set_configuration(configs[0]) - except USBError: - self.handle.set_configuration(configs[1]) - config = configs[1] - _id = config.interface.contents.altsetting.contents - ed1 = _id.endpoint[0] - ed2 = _id.endpoint[1] - if ed1.EndpointAddress == self.BULK_IN_EP: - red, wed = ed1, ed2 - else: - red, wed = ed2, ed1 - self.bulk_read_max_packet_size = red.MaxPacketSize - self.bulk_write_max_packet_size = wed.MaxPacketSize - self.handle.claim_interface(self.INTERFACE_ID) - except USBError as err: - raise DeviceBusy(str(err)) - # Large timeout as device may still be initializing - res = self.send_validated_command(GetUSBProtocolVersion(), timeout=20000) - if res.code != 0: - raise ProtocolError("Unable to get USB Protocol version.") - version = self._bulk_read(24, data_type=USBProtocolVersion)[0].version - if version not in KNOWN_USB_PROTOCOL_VERSIONS: - print >> sys.stderr, "WARNING: Usb protocol version " + \ - hex(version) + " is unknown" - res = self.send_validated_command(SetBulkSize(\ - chunk_size = 512*self.bulk_read_max_packet_size, \ - unknown = 2)) - if res.code != 0: - raise ProtocolError("Unable to set bulk size.") - res = self.send_validated_command(UnlockDevice(key=self.key))#0x312d)) - if res.code != 0: - raise DeviceLocked() - res = self.send_validated_command(SetTime()) - if res.code != 0: - raise ProtocolError("Could not set time on device") - - def eject(self): - pass - - def close(self): - """ Release device interface """ - with lock: - try: - self.handle.reset() - self.handle.release_interface(self.INTERFACE_ID) - except Exception as err: - print >> sys.stderr, err - self.handle, self.device = None, None - self.in_session = False - - def _send_command(self, command, response_type=Response, timeout=1000): - """ - Send L{command} to device and return its L{response}. - - @param command: an object of type Command or one of its derived classes - @param response_type: an object of type 'type'. The return packet - from the device is returned as an object of type response_type. - @param timeout: The time to wait for a response from the - device, in milliseconds. If there is no response, a L{usb.USBError} is raised. - """ - with lock: - if self.log_packets: - self.log_packet(command, "Command") - bytes_sent = self.handle.control_msg(0x40, 0x80, command) - if bytes_sent != len(command): - raise ControlError(desc="Could not send control request to device\n"\ - + str(command)) - response = response_type(self.handle.control_msg(0xc0, 0x81, \ - Response.SIZE, timeout=timeout)) - if self.log_packets: - self.log_packet(response, "Response") - return response - - def send_validated_command(self, command, cnumber=None, \ - response_type=Response, timeout=1000): - """ - Wrapper around L{_send_command} that checks if the - C{Response.rnumber == cnumber or - command.number if cnumber==None}. Also check that - C{Response.type == Command.type}. - """ - if cnumber == None: - cnumber = command.number - res = self._send_command(command, response_type=response_type, \ - timeout=timeout) - self.validate_response(res, _type=command.type, number=cnumber) - return res - - def _bulk_write(self, data, packet_size=0x1000): - """ - Send data to device via a bulk transfer. - @type data: Any listable type supporting __getslice__ - @param packet_size: Size of packets to be sent to device. - C{data} is broken up into packets to be sent to device. - """ - with lock: - def bulk_write_packet(packet): - self.handle.bulk_write(self.BULK_OUT_EP, packet) - if self.log_packets: - self.log_packet(Answer(packet), "Answer h->d") - - bytes_left = len(data) - if bytes_left + 16 <= packet_size: - packet_size = bytes_left +16 - first_packet = Answer(bytes_left+16) - first_packet[16:] = data - first_packet.length = len(data) - else: - first_packet = Answer(packet_size) - first_packet[16:] = data[0:packet_size-16] - first_packet.length = packet_size-16 - first_packet.number = 0x10005 - bulk_write_packet(first_packet) - pos = first_packet.length - bytes_left -= first_packet.length - while bytes_left > 0: - endpos = pos + packet_size if pos + packet_size <= len(data) \ - else len(data) - bulk_write_packet(data[pos:endpos]) - bytes_left -= endpos - pos - pos = endpos - res = Response(self.handle.control_msg(0xc0, 0x81, Response.SIZE, \ - timeout=5000)) - if self.log_packets: - self.log_packet(res, "Response") - if res.rnumber != 0x10005 or res.code != 0: - raise ProtocolError("Sending via Bulk Transfer failed with response:\n"\ - +str(res)) - if res.data_size != len(data): - raise ProtocolError("Unable to transfer all data to device. "+\ - "Response packet:\n"\ - +str(res)) - - - def _bulk_read(self, bytes, command_number=0x00, packet_size=0x1000, \ - data_type=Answer): - """ - Read in C{bytes} bytes via a bulk transfer in - packets of size S{<=} C{packet_size} - @param data_type: an object of type type. - The data packet is returned as an object of type C{data_type}. - @return: A list of packets read from the device. - Each packet is of type data_type - """ - with lock: - msize = self.bulk_read_max_packet_size - def bulk_read_packet(data_type=Answer, size=0x1000): - rsize = size - if size % msize: - rsize = size - size % msize + msize - data = data_type(self.handle.bulk_read(self.BULK_IN_EP, rsize)) - if self.log_packets: - self.log_packet(data, "Answer d->h") - if len(data) != size: - raise ProtocolError("Unable to read " + str(size) + " bytes from "\ - "device. Read: " + str(len(data)) + " bytes") - return data - - bytes_left = bytes - packets = [] - while bytes_left > 0: - if packet_size > bytes_left: - packet_size = bytes_left - packet = bulk_read_packet(data_type=data_type, size=packet_size) - bytes_left -= len(packet) - packets.append(packet) - self.send_validated_command(\ - AcknowledgeBulkRead(packets[0].number), \ - cnumber=command_number) - return packets - - @safe - def get_device_information(self, end_session=True): - """ - Ask device for device information. See L{DeviceInfoQuery}. - @return: (device name, device version, software version on device, mime type) - """ - size = self.send_validated_command(DeviceInfoQuery()).data[2] + 16 - ans = self._bulk_read(size, command_number=\ - DeviceInfoQuery.NUMBER, data_type=DeviceInfo)[0] - return (ans.device_name, ans.device_version, \ - ans.software_version, ans.mime_type) - - @safe - def path_properties(self, path, end_session=True): - """ - Send command asking device for properties of C{path}. - Return L{FileProperties}. - """ - res = self.send_validated_command(PathQuery(path), \ - response_type=ListResponse) - data = self._bulk_read(0x28, data_type=FileProperties, \ - command_number=PathQuery.NUMBER)[0] - if path.endswith('/') and path != '/': - path = path[:-1] - if res.path_not_found : - raise PathError(path + " does not exist on device") - if res.is_invalid: - raise PathError(path + " is not a valid path") - if res.is_unmounted: - raise PathError(path + " is not mounted") - if res.permission_denied: - raise PathError('Permission denied for: ' + path + '\nYou can only '+\ - 'operate on paths starting with /Data, a:/ or b:/') - if res.code not in (0, PathResponseCodes.IS_FILE): - raise PathError(path + " has an unknown error. Code: " + \ - hex(res.code)) - return data - - @safe - def get_file(self, path, outfile, end_session=True): - """ - Read the file at path on the device and write it to outfile. - - The data is fetched in chunks of size S{<=} 32K. Each chunk is - made of packets of size S{<=} 4K. See L{FileOpen}, - L{FileRead} and L{FileClose} for details on the command packets used. - - @param outfile: file object like C{sys.stdout} or the result of an C{open} call - """ - if path.endswith("/"): - path = path[:-1] # We only copy files - cp = self.card_prefix(False) - path = path.replace('card:/', cp if cp else '') - _file = self.path_properties(path, end_session=False) - if _file.is_dir: - raise PathError("Cannot read as " + path + " is a directory") - bytes = _file.file_size - res = self.send_validated_command(FileOpen(path)) - if res.code != 0: - raise PathError("Unable to open " + path + \ - " for reading. Response code: " + hex(res.code)) - _id = self._bulk_read(20, data_type=IdAnswer, \ - command_number=FileOpen.NUMBER)[0].id - # The first 16 bytes from the device are meta information on the packet stream - bytes_left, chunk_size = bytes, 512 * self.bulk_read_max_packet_size -16 - packet_size, pos = 64 * self.bulk_read_max_packet_size, 0 - while bytes_left > 0: - if chunk_size > bytes_left: - chunk_size = bytes_left - res = self.send_validated_command(FileIO(_id, pos, chunk_size)) - if res.code != 0: - self.send_validated_command(FileClose(id)) - raise ProtocolError("Error while reading from " + path + \ - ". Response code: " + hex(res.code)) - packets = self._bulk_read(chunk_size+16, \ - command_number=FileIO.RNUMBER, packet_size=packet_size) - try: - outfile.write("".join(map(chr, packets[0][16:]))) - for i in range(1, len(packets)): - outfile.write("".join(map(chr, packets[i]))) - except IOError as err: - self.send_validated_command(FileClose(_id)) - raise ArgumentError("File get operation failed. " + \ - "Could not write to local location: " + str(err)) - bytes_left -= chunk_size - pos += chunk_size - if self.report_progress: - self.report_progress(int(100*((1.*pos)/bytes))) - self.send_validated_command(FileClose(_id)) - # Not going to check response code to see if close was successful - # as there's not much we can do if it wasnt - - @safe - def list(self, path, recurse=False, end_session=True): - """ - Return a listing of path. See the code for details. See L{DirOpen}, - L{DirRead} and L{DirClose} for details on the command packets used. - - @type path: string - @param path: The path to list - @type recurse: boolean - @param recurse: If true do a recursive listing - @return: A list of tuples. The first element of each tuple is a path. - The second element is a list of L{Files}. - The path is the path we are listing, the C{Files} are the - files/directories in that path. If it is a recursive list, then the first - element will be (C{path}, children), the next will be - (child, its children) and so on. If it is not recursive the length of the - outermost list will be 1. - """ - def _list(path): - """ Do a non recursive listsing of path """ - if not path.endswith("/"): - path += "/" # Initially assume path is a directory - cp = self.card_prefix(False) - path = path.replace('card:/', cp if cp else '') - files = [] - candidate = self.path_properties(path, end_session=False) - if not candidate.is_dir: - path = path[:-1] - data = self.path_properties(path, end_session=False) - files = [ File((path, data)) ] - else: - # Get query ID used to ask for next element in list - res = self.send_validated_command(DirOpen(path)) - if res.code != 0: - raise PathError("Unable to open directory " + path + \ - " for reading. Response code: " + hex(res.code)) - _id = self._bulk_read(0x14, data_type=IdAnswer, \ - command_number=DirOpen.NUMBER)[0].id - # Create command asking for next element in list - next = DirRead(_id) - items = [] - while True: - res = self.send_validated_command(next, response_type=ListResponse) - size = res.data_size + 16 - data = self._bulk_read(size, data_type=ListAnswer, \ - command_number=DirRead.NUMBER)[0] - # path_not_found seems to happen if the usb server - # doesn't have the permissions to access the directory - if res.is_eol or res.path_not_found: - break - elif res.code != 0: - raise ProtocolError("Unknown error occured while "+\ - "reading contents of directory " + path + \ - ". Response code: " + hex(res.code)) - items.append(data.name) - self.send_validated_command(DirClose(_id)) - # Ignore res.code as we cant do anything if close fails - for item in items: - ipath = path + item - data = self.path_properties(ipath, end_session=False) - files.append( File( (ipath, data) ) ) - files.sort() - return files - - files = _list(path) - dirs = [(path, files)] - - for _file in files: - if recurse and _file.is_dir and not _file.path.startswith(("/dev","/proc")): - dirs[len(dirs):] = self.list(_file.path, recurse=True, end_session=False) - return dirs - - @safe - def total_space(self, end_session=True): - """ - Get total space available on the mountpoints: - 1. Main memory - 2. Memory Stick - 3. SD Card - - @return: A 3 element list with total space in bytes of (1, 2, 3) - """ - data = [] - for path in ("/Data/", "a:/", "b:/"): - # Timeout needs to be increased as it takes time to read card - res = self.send_validated_command(TotalSpaceQuery(path), \ - timeout=5000) - buffer_size = 16 + res.data[2] - pkt = self._bulk_read(buffer_size, data_type=TotalSpaceAnswer, \ - command_number=TotalSpaceQuery.NUMBER)[0] - data.append( pkt.total ) - return data - - @safe - def card_prefix(self, end_session=True): - try: - path = 'a:/' - self.path_properties(path, end_session=False) - return path - except PathError: - try: - path = 'b:/' - self.path_properties(path, end_session=False) - return path - except PathError: - return None - - @safe - def free_space(self, end_session=True): - """ - Get free space available on the mountpoints: - 1. Main memory - 2. Memory Stick - 3. SD Card - - @return: A 3 element list with free space in bytes of (1, 2, 3) - """ - data = [] - for path in ("/", "a:/", "b:/"): - # Timeout needs to be increased as it takes time to read card - self.send_validated_command(FreeSpaceQuery(path), \ - timeout=5000) - pkt = self._bulk_read(FreeSpaceAnswer.SIZE, \ - data_type=FreeSpaceAnswer, \ - command_number=FreeSpaceQuery.NUMBER)[0] - data.append( pkt.free ) - data = [x for x in data if x != 0] - data.append(0) - return data - - def _exists(self, path): - """ Return (True, FileProperties) if path exists or (False, None) otherwise """ - dest = None - try: - dest = self.path_properties(path, end_session=False) - except PathError as err: - if "does not exist" in str(err) or "not mounted" in str(err): - return (False, None) - else: raise - return (True, dest) - - @safe - def touch(self, path, end_session=True): - """ - Create a file at path - @todo: Update file modification time if it exists. - Opening the file in write mode and then closing it doesn't work. - """ - cp = self.card_prefix(False) - path = path.replace('card:/', cp if cp else '') - if path.endswith("/") and len(path) > 1: - path = path[:-1] - exists, _file = self._exists(path) - if exists and _file.is_dir: - raise PathError("Cannot touch directories") - if not exists: - res = self.send_validated_command(FileCreate(path)) - if res.code != 0: - raise PathError("Could not create file " + path + \ - ". Response code: " + str(hex(res.code))) - - @safe - def put_file(self, infile, path, replace_file=False, end_session=True): - """ - Put infile onto the devoce at path - @param infile: An open file object. infile must have a name attribute. - If you are using a StringIO object set its name attribute manually. - @param path: The path on the device at which to put infile. - It should point to an existing directory. - @param replace_file: If True and path points to a file that already exists, it is replaced - """ - pos = infile.tell() - infile.seek(0, 2) - bytes = infile.tell() - pos - start_pos = pos - infile.seek(pos) - cp = self.card_prefix(False) - path = path.replace('card:/', cp if cp else '') - exists, dest = self._exists(path) - if exists: - if dest.is_dir: - if not path.endswith("/"): - path += "/" - path += os.path.basename(infile.name) - return self.put_file(infile, path, replace_file=replace_file, end_session=False) - else: - if not replace_file: - raise PathError("Cannot write to " + \ - path + " as it already exists", path=path) - _file = self.path_properties(path, end_session=False) - if _file.file_size > bytes: - self.del_file(path, end_session=False) - self.touch(path, end_session=False) - else: self.touch(path, end_session=False) - chunk_size = 512 * self.bulk_write_max_packet_size - data_left = True - res = self.send_validated_command(FileOpen(path, mode=FileOpen.WRITE)) - if res.code != 0: - raise ProtocolError("Unable to open " + path + \ - " for writing. Response code: " + hex(res.code)) - _id = self._bulk_read(20, data_type=IdAnswer, \ - command_number=FileOpen.NUMBER)[0].id - - while data_left: - data = array('B') - try: - # Cannot use data.fromfile(infile, chunk_size) as it - # doesn't work in windows w/ python 2.5.1 - ind = infile.read(chunk_size) - data.fromstring(ind) - if len(ind) < chunk_size: - raise EOFError - except EOFError: - data_left = False - res = self.send_validated_command(FileIO(_id, pos, len(data), \ - mode=FileIO.WNUMBER)) - if res.code != 0: - raise ProtocolError("Unable to write to " + \ - path + ". Response code: " + hex(res.code)) - self._bulk_write(data) - pos += len(data) - if self.report_progress: - self.report_progress( int(100*(pos-start_pos)/(1.*bytes)) ) - self.send_validated_command(FileClose(_id)) - # Ignore res.code as cant do anything if close fails - _file = self.path_properties(path, end_session=False) - if _file.file_size != pos: - raise ProtocolError("Copying to device failed. The file " +\ - "on the device is larger by " + \ - str(_file.file_size - pos) + " bytes") - - @safe - def del_file(self, path, end_session=True): - """ Delete C{path} from device iff path is a file """ - data = self.path_properties(path, end_session=False) - if data.is_dir: - raise PathError("Cannot delete directories") - res = self.send_validated_command(FileDelete(path), \ - response_type=ListResponse) - if res.code != 0: - raise ProtocolError("Unable to delete " + path + \ - " with response:\n" + str(res)) - - @safe - def mkdir(self, path, end_session=True): - """ Make directory """ - if path.startswith('card:/'): - cp = self.card_prefix(False) - path = path.replace('card:/', cp if cp else '') - if not path.endswith("/"): - path += "/" - error_prefix = "Cannot create directory " + path - res = self.send_validated_command(DirCreate(path)).data[0] - if res == 0xffffffcc: - raise PathError(error_prefix + " as it already exists") - elif res == PathResponseCodes.NOT_FOUND: - raise PathError(error_prefix + " as " + \ - path[0:path[:-1].rfind("/")] + " does not exist ") - elif res == PathResponseCodes.INVALID: - raise PathError(error_prefix + " as " + path + " is invalid") - elif res != 0: - raise PathError(error_prefix + ". Response code: " + hex(res)) - - @safe - def rm(self, path, end_session=True): - """ Delete path from device if it is a file or an empty directory """ - cp = self.card_prefix(False) - path = path.replace('card:/', cp if cp else '') - dir = self.path_properties(path, end_session=False) - if not dir.is_dir: - self.del_file(path, end_session=False) - else: - if not path.endswith("/"): - path += "/" - res = self.send_validated_command(DirDelete(path)) - if res.code == PathResponseCodes.HAS_CHILDREN: - raise PathError("Cannot delete directory " + path + \ - " as it is not empty") - if res.code != 0: - raise ProtocolError("Failed to delete directory " + path + \ - ". Response code: " + hex(res.code)) - - @safe - def card(self, end_session=True): - """ Return path prefix to installed card or None """ - card = None - try: - if self._exists("a:/")[0]: - card = "a:" - except: - pass - try: - if self._exists("b:/")[0]: - card = "b:" - except: - pass - return card - - @safe - def books(self, oncard=False, end_session=True): - """ - Return a list of ebooks on the device. - @param oncard: If True return a list of ebooks on the storage card, - otherwise return list of ebooks in main memory of device - - @return: L{BookList} - """ - root = "/Data/media/" - tfile = TemporaryFile() - if oncard: - try: - self.get_file("a:"+self.CACHE_XML, tfile, end_session=False) - root = "a:/" - except PathError: - try: - self.get_file("b:"+self.CACHE_XML, tfile, end_session=False) - root = "b:/" - except PathError: pass - if tfile.tell() == 0: - tfile = None - else: - self.get_file(self.MEDIA_XML, tfile, end_session=False) - bl = BookList(root=root, sfile=tfile) - paths = bl.purge_corrupted_files() - for path in paths: - try: - self.del_file(path, end_session=False) - except PathError: # Incase this is a refetch without a sync in between - continue - return bl - - @safe - def remove_books(self, paths, booklists, end_session=True): - """ - Remove the books specified by paths from the device. The metadata - cache on the device should also be updated. - """ - for path in paths: - self.del_file(path, end_session=False) - fix_ids(booklists[0], booklists[1]) - self.sync_booklists(booklists, end_session=False) - - @safe - def sync_booklists(self, booklists, end_session=True): - ''' - Upload bookslists to device. - @param booklists: A tuple containing the result of calls to - (L{books}(oncard=False), L{books}(oncard=True)). - ''' - fix_ids(*booklists) - self.upload_book_list(booklists[0], end_session=False) - if booklists[1].root: - self.upload_book_list(booklists[1], end_session=False) - - @safe - def upload_books(self, files, names, on_card=False, end_session=True, - metadata=None): - card = self.card(end_session=False) - prefix = card + '/' + self.CARD_PATH_PREFIX +'/' if on_card else '/Data/media/books/' - if on_card and not self._exists(prefix)[0]: - self.mkdir(prefix[:-1], False) - paths, ctimes = [], [] - names = iter(names) - infiles = [file if hasattr(file, 'read') else open(file, 'rb') for file in files] - for f in infiles: f.seek(0, 2) - sizes = [f.tell() for f in infiles] - size = sum(sizes) - space = self.free_space(end_session=False) - mspace = space[0] - cspace = space[2] if len(space) > 2 and space[2] >= space[1] else space[1] - if on_card and size > cspace - 1024*1024: - raise FreeSpaceError("There is insufficient free space "+\ - "on the storage card") - if not on_card and size > mspace - 2*1024*1024: - raise FreeSpaceError("There is insufficient free space " +\ - "in main memory") - - for infile in infiles: - infile.seek(0) - name = names.next() - paths.append(prefix+name) - self.put_file(infile, paths[-1], replace_file=True, end_session=False) - ctimes.append(self.path_properties(paths[-1], end_session=False).ctime) - return zip(paths, sizes, ctimes) - - @classmethod - def add_books_to_metadata(cls, locations, metadata, booklists): - metadata = iter(metadata) - for location in locations: - info = metadata.next() - path = location[0] - on_card = 1 if path[1] == ':' else 0 - name = path.rpartition('/')[2] - name = (cls.CARD_PATH_PREFIX+'/' if on_card else 'books/') + name - booklists[on_card].add_book(info, name, *location[1:]) - fix_ids(*booklists) - - @safe - def delete_books(self, paths, end_session=True): - for path in paths: - self.del_file(path, end_session=False) - - @classmethod - def remove_books_from_metadata(cls, paths, booklists): - for path in paths: - on_card = 1 if path[1] == ':' else 0 - booklists[on_card].remove_book(path) - fix_ids(*booklists) - - @safe - def add_book(self, infile, name, info, booklists, oncard=False, \ - sync_booklists=False, end_session=True): - """ - Add a book to the device. If oncard is True then the book is copied - to the card rather than main memory. - - @param infile: The source file, should be opened in "rb" mode - @param name: The name of the book file when uploaded to the - device. The extension of name must be one of - the supported formats for this device. - @param info: A dictionary that must have the keys "title", "authors", "cover". - C{info["cover"]} should be a three element tuple (width, height, data) - where data is the image data in JPEG format as a string - @param booklists: A tuple containing the result of calls to - (L{books}(oncard=False), L{books}(oncard=True)). - """ - infile.seek(0, 2) - size = infile.tell() - infile.seek(0) - card = self.card(end_session=False) - space = self.free_space(end_session=False) - mspace = space[0] - cspace = space[1] if space[1] >= space[2] else space[2] - if oncard and size > cspace - 1024*1024: - raise FreeSpaceError("There is insufficient free space "+\ - "on the storage card") - if not oncard and size > mspace - 1024*1024: - raise FreeSpaceError("There is insufficient free space " +\ - "in main memory") - prefix = "/Data/media/" - if oncard: - prefix = card + "/" - else: name = "books/"+name - path = prefix + name - self.put_file(infile, path, end_session=False) - ctime = self.path_properties(path, end_session=False).ctime - bkl = booklists[1] if oncard else booklists[0] - bkl.add_book(info, name, size, ctime) - fix_ids(booklists[0], booklists[1]) - if sync_booklists: - self.sync_booklists(booklists, end_session=False) - - @safe - def upload_book_list(self, booklist, end_session=True): - path = self.MEDIA_XML - if not booklist.prefix: - card = self.card(end_session=True) - if not card: - raise ArgumentError("Cannot upload list to card as "+\ - "card is not present") - path = card + self.CACHE_XML - f = StringIO() - booklist.write(f) - f.seek(0) - self.put_file(f, path, replace_file=True, end_session=False) - f.close() diff --git a/src/calibre/devices/prs500/prstypes.py b/src/calibre/devices/prs500/prstypes.py deleted file mode 100755 index 3efbfcab31..0000000000 --- a/src/calibre/devices/prs500/prstypes.py +++ /dev/null @@ -1,861 +0,0 @@ -__license__ = 'GPL v3' -__copyright__ = '2008, Kovid Goyal ' -""" -Defines the structure of packets that are sent to/received from the device. - -Packet structure is defined using classes and inheritance. Each class is a -view that imposes structure on the underlying data buffer. -The data buffer is encoded in little-endian format, but you don't -have to worry about that if you are using the classes. -The classes have instance variables with getter/setter functions defined -to take care of the encoding/decoding. -The classes are intended to mimic C structs. - -There are three kinds of packets. L{Commands}, -L{Responses}, and L{Answers}. -C{Commands} are sent to the device on the control bus, -C{Responses} are received from the device, -also on the control bus. C{Answers} and their sub-classes represent -data packets sent to/received from the device via bulk transfers. - -Commands are organized as follows: G{classtree Command} - -You will typically only use sub-classes of Command. - -Responses are organized as follows: G{classtree Response} - -Responses inherit Command as they share header structure. - -Answers are organized as follows: G{classtree Answer} -""" - -import struct -import time -from datetime import datetime - -from calibre.devices.errors import PacketError - -WORD = " 31 and self[i+b] < 127 else "." - except IndexError: break - ans = ans + " " - if (i+2)%16 == 0: - if i+2 < len(self): - ans += " " + ascii + "\n" + (TransferBuffer.phex(i+2)+": ").rjust(10, "0") - ascii = "" - last_line = ans[ans.rfind("\n")+1:] - padding = 50 - len(last_line) - ans += "".ljust(padding) + " " + ascii - return ans.strip() - - def unpack(self, fmt=DWORD, start=0): - """ - Return decoded data from buffer. - - @param fmt: See U{struct} - @param start: Position in buffer from which to decode - """ - end = start + struct.calcsize(fmt) - return struct.unpack(fmt, "".join([ chr(i) for i in list.__getslice__(self, start, end) ])) - - def pack(self, val, fmt=DWORD, start=0): - """ - Encode C{val} and write it to buffer. For fmt==WORD val is - adjusted to be in the range 0 <= val < 256**2. - - @param fmt: See U{struct} - @param start: Position in buffer at which to write encoded data - """ - # struct.py is fussy about packing values into a WORD. The value must be - # between 0 and 65535 or a DeprecationWarning is raised. In the future - # this may become an error, so it's best to take care of wrapping here. - if fmt == WORD: - val = val % 256**2 - self[start:start+struct.calcsize(fmt)] = \ - [ ord(i) for i in struct.pack(fmt, val) ] - - def _normalize(self): - """ Replace negative bytes in C{self} by 256 + byte """ - for i in range(len(self)): - if self[i] < 0: - self[i] = 256 + self[i] - - @classmethod - def phex(cls, num): - """ - Return the hex representation of num without the 0x prefix. - - If the hex representation is only 1 digit it is padded to the left with a zero. Used in L{TransferBuffer.__str__}. - """ - index, sign = 2, "" - if num < 0: - index, sign = 3, "-" - h = hex(num)[index:] - if len(h) < 2: - h = "0"+h - return sign + h - - -class field(object): - """ A U{Descriptor}, that implements access - to protocol packets in a human readable way. - """ - def __init__(self, start=16, fmt=DWORD): - """ - @param start: The byte at which this field is stored in the buffer - @param fmt: The packing format for this field. - See U{struct}. - """ - self._fmt, self._start = fmt, start - - def __get__(self, obj, typ=None): - return obj.unpack(start=self._start, fmt=self._fmt)[0] - - def __set__(self, obj, val): - obj.pack(val, start=self._start, fmt=self._fmt) - - def __repr__(self): - typ = "" - if self._fmt == DWORD: - typ = "unsigned int" - if self._fmt == DDWORD: - typ = "unsigned long long" - return "An " + typ + " stored in " + \ - str(struct.calcsize(self._fmt)) + \ - " bytes starting at byte " + str(self._start) - -class stringfield(object): - """ A field storing a variable length string. """ - def __init__(self, length_field, start=16): - """ - @param length_field: A U{Descriptor} - that returns the length of the string. - @param start: The byte at which this field is stored in the buffer - """ - self._length_field = length_field - self._start = start - - def __get__(self, obj, typ=None): - length = str(self._length_field.__get__(obj)) - return obj.unpack(start=self._start, fmt="<"+length+"s")[0] - - def __set__(self, obj, val): - if isinstance(val, unicode): - val = val.encode('utf8') - else: - val = str(val) - obj.pack(val, start=self._start, fmt="<"+str(len(val))+"s") - - def __repr__(self): - return "A string starting at byte " + str(self._start) - -class Command(TransferBuffer): - - """ Defines the structure of command packets sent to the device. """ - # Command number. C{unsigned int} stored in 4 bytes at byte 0. - # - # Command numbers are: - # 0 GetUsbProtocolVersion - # 1 ReqEndSession - # 10 FskFileOpen - # 11 FskFileClose - # 12 FskGetSize - # 13 FskSetSize - # 14 FskFileSetPosition - # 15 FskGetPosition - # 16 FskFileRead - # 17 FskFileWrite - # 18 FskFileGetFileInfo - # 19 FskFileSetFileInfo - # 1A FskFileCreate - # 1B FskFileDelete - # 1C FskFileRename - # 30 FskFileCreateDirectory - # 31 FskFileDeleteDirectory - # 32 FskFileRenameDirectory - # 33 FskDirectoryIteratorNew - # 34 FskDirectoryIteratorDispose - # 35 FskDirectoryIteratorGetNext - # 52 FskVolumeGetInfo - # 53 FskVolumeGetInfoFromPath - # 80 FskFileTerminate - # 100 ConnectDevice - # 101 GetProperty - # 102 GetMediaInfo - # 103 GetFreeSpace - # 104 SetTime - # 105 DeviceBeginEnd - # 106 UnlockDevice - # 107 SetBulkSize - # 110 GetHttpRequest - # 111 SetHttpRespponse - # 112 Needregistration - # 114 GetMarlinState - # 200 ReqDiwStart - # 201 SetDiwPersonalkey - # 202 GetDiwPersonalkey - # 203 SetDiwDhkey - # 204 GetDiwDhkey - # 205 SetDiwChallengeserver - # 206 GetDiwChallengeserver - # 207 GetDiwChallengeclient - # 208 SetDiwChallengeclient - # 209 GetDiwVersion - # 20A SetDiwWriteid - # 20B GetDiwWriteid - # 20C SetDiwSerial - # 20D GetDiwModel - # 20C SetDiwSerial - # 20E GetDiwDeviceid - # 20F GetDiwSerial - # 210 ReqDiwCheckservicedata - # 211 ReqDiwCheckiddata - # 212 ReqDiwCheckserialdata - # 213 ReqDiwFactoryinitialize - # 214 GetDiwMacaddress - # 215 ReqDiwTest - # 216 ReqDiwDeletekey - # 300 UpdateChangemode - # 301 UpdateDeletePartition - # 302 UpdateCreatePartition - # 303 UpdateCreatePartitionWithImage - # 304 UpdateGetPartitionSize - number = field(start=0, fmt=DWORD) - # Known types are 0x00 and 0x01. Acknowledge commands are always type 0x00 - type = field(start=4, fmt=DDWORD) - # Length of the data part of this packet - length = field(start=12, fmt=DWORD) - - @dynamic_property - def data(self): - doc = \ - """ - The data part of this command. Returned/set as/by a TransferBuffer. - Stored at byte 16. - - Setting it by default changes self.length to the length of the new - buffer. You may have to reset it to the significant part of the buffer. - You would normally use the C{command} property of - L{ShortCommand} or L{LongCommand} instead. - """ - def fget(self): - return self[16:] - - def fset(self, buff): - self[16:] = buff - self.length = len(buff) - - return property(doc=doc, fget=fget, fset=fset) - - def __init__(self, packet): - """ - @param packet: len(packet) > 15 or packet > 15 - """ - if ("__len__" in dir(packet) and len(packet) < 16) or\ - ("__len__" not in dir(packet) and packet < 16): - raise PacketError(str(self.__class__)[7:-2] + \ - " packets must have length atleast 16") - TransferBuffer.__init__(self, packet) - - -class SetTime(Command): - """ - Set time on device. All fields refer to time in the GMT time zone. - """ - NUMBER = 0x104 - # -time.timezone with negative numbers encoded - # as int(0xffffffff +1 -time.timezone/60.) - timezone = field(start=0x10, fmt=DWORD) - year = field(start=0x14, fmt=DWORD) #: year e.g. 2006 - month = field(start=0x18, fmt=DWORD) #: month 1-12 - day = field(start=0x1c, fmt=DWORD) #: day 1-31 - hour = field(start=0x20, fmt=DWORD) #: hour 0-23 - minute = field(start=0x24, fmt=DWORD) #: minute 0-59 - second = field(start=0x28, fmt=DWORD) #: second 0-59 - - def __init__(self, t=None): - """ @param t: time as an epoch """ - self.number = SetTime.NUMBER - self.type = 0x01 - self.length = 0x1c - td = datetime.now() - datetime.utcnow() - tz = int((td.days*24*3600 + td.seconds)/60.) - self.timezone = tz if tz > 0 else 0xffffffff +1 + tz - if not t: t = time.time() - t = time.gmtime(t) - self.year = t[0] - self.month = t[1] - self.day = t[2] - self.hour = t[3] - self.minute = t[4] - # Hack you should actually update the entire time tree if - # second is > 59 - self.second = t[5] if t[5] < 60 else 59 - - -class ShortCommand(Command): - - """ A L{Command} whose data section is 4 bytes long """ - - SIZE = 20 #: Packet size in bytes - # Usually carries additional information - command = field(start=16, fmt=DWORD) - - def __init__(self, number=0x00, type=0x00, command=0x00): - """ - @param number: L{Command.number} - @param type: L{Command.type} - @param command: L{ShortCommand.command} - """ - Command.__init__(self, ShortCommand.SIZE) - self.number = number - self.type = type - self.length = 4 - self.command = command - -class DirRead(ShortCommand): - """ The command that asks the device to send the next item in the list """ - NUMBER = 0x35 #: Command number - def __init__(self, _id): - """ @param id: The identifier returned as a result of a L{DirOpen} command """ - ShortCommand.__init__(self, number=DirRead.NUMBER, type=0x01, \ - command=_id) - -class DirClose(ShortCommand): - """ Close a previously opened directory """ - NUMBER = 0x34 #: Command number - def __init__(self, _id): - """ @param id: The identifier returned as a result of a L{DirOpen} command """ - ShortCommand.__init__(self, number=DirClose.NUMBER, type=0x01, - command=_id) - -class BeginEndSession(ShortCommand): - """ - Ask device to either start or end a session. - """ - NUMBER = 0x01 #: Command number - def __init__(self, end=True): - command = 0x00 if end else 0x01 - ShortCommand.__init__(self, \ - number=BeginEndSession.NUMBER, type=0x01, command=command) - -class GetUSBProtocolVersion(ShortCommand): - """ Get USB Protocol version used by device """ - NUMBER = 0x0 #: Command number - def __init__(self): - ShortCommand.__init__(self, \ - number=GetUSBProtocolVersion.NUMBER, \ - type=0x01, command=0x00) - -class SetBulkSize(Command): - """ Set size for bulk transfers in this session """ - NUMBER = 0x107 #: Command number - chunk_size = field(fmt=WORD, start=0x10) - unknown = field(fmt=WORD, start=0x12) - def __init__(self, chunk_size=0x8000, unknown=0x2): - Command.__init__(self, [0 for i in range(24)]) - self.number = SetBulkSize.NUMBER - self.type = 0x01 - self.chunk_size = chunk_size - self.unknown = unknown - -class UnlockDevice(Command): - """ Unlock the device """ - NUMBER = 0x106 #: Command number - key = stringfield(8, start=16) #: The key defaults to -1 - - def __init__(self, key='-1\0\0\0\0\0\0'): - Command.__init__(self, 24) - self.number = UnlockDevice.NUMBER - self.type = 0x01 - self.length = 8 - self.key = key - -class LongCommand(Command): - - """ A L{Command} whose data section is 16 bytes long """ - - SIZE = 32 #: Size in bytes of C{LongCommand} packets - - def __init__(self, number=0x00, type=0x00, command=0x00): - """ - @param number: L{Command.number} - @param type: L{Command.type} - @param command: L{LongCommand.command} - """ - Command.__init__(self, LongCommand.SIZE) - self.number = number - self.type = type - self.length = 16 - self.command = command - - @dynamic_property - def command(self): - doc = \ - """ - Usually carries extra information needed for the command - It is a list of C{unsigned integers} of length between 1 and 4. 4 - C{unsigned int} stored in 16 bytes at byte 16. - """ - def fget(self): - return self.unpack(start=16, fmt="<"+str(self.length/4)+"I") - - def fset(self, val): - if "__len__" not in dir(val): val = (val,) - start = 16 - for command in val: - self.pack(command, start=start, fmt=DWORD) - start += struct.calcsize(DWORD) - - return property(doc=doc, fget=fget, fset=fset) - -class PathCommand(Command): - """ Abstract class that defines structure common to all path related commands. """ - - path_length = field(start=16, fmt=DWORD) #: Length of the path to follow - path = stringfield(path_length, start=20) #: The path this query is about - def __init__(self, path, number, path_len_at_byte=16): - Command.__init__(self, path_len_at_byte+4+len(path)) - if isinstance(path, unicode): - path = path.encode('utf8') - self.path_length = len(path) - self.path = path - self.type = 0x01 - self.length = len(self) - 16 - self.number = number - -class TotalSpaceQuery(PathCommand): - """ Query the total space available on the volume represented by path """ - NUMBER = 0x53 #: Command number - def __init__(self, path): - """ @param path: valid values are 'a:', 'b:', '/Data/' """ - PathCommand.__init__(self, path, TotalSpaceQuery.NUMBER) - -class FreeSpaceQuery(ShortCommand): - """ Query the free space available """ - NUMBER = 0x103 #: Command number - def __init__(self, where): - """ @param where: valid values are: 'a:', 'b:', '/' """ - c = 0 - if where.startswith('a:'): c = 1 - elif where.startswith('b:'): c = 2 - ShortCommand.__init__(self, \ - number=FreeSpaceQuery.NUMBER, type=0x01, command=c) - -class DirCreate(PathCommand): - """ Create a directory """ - NUMBER = 0x30 - def __init__(self, path): - PathCommand.__init__(self, path, DirCreate.NUMBER) - -class DirOpen(PathCommand): - """ Open a directory for reading its contents """ - NUMBER = 0x33 #: Command number - def __init__(self, path): - PathCommand.__init__(self, path, DirOpen.NUMBER) - - -class AcknowledgeBulkRead(LongCommand): - """ Must be sent to device after a bulk read """ - def __init__(self, bulk_read_id): - """ - bulk_read_id is an integer, the id of the bulk read - we are acknowledging. See L{Answer.id} - """ - LongCommand.__init__(self, number=0x1000, \ - type=0x00, command=bulk_read_id) - -class DeviceInfoQuery(Command): - """ The command used to ask for device information """ - NUMBER = 0x101 #: Command number - def __init__(self): - Command.__init__(self, 16) - self.number = DeviceInfoQuery.NUMBER - self.type = 0x01 - -class FileClose(ShortCommand): - """ File close command """ - NUMBER = 0x11 #: Command number - def __init__(self, _id): - ShortCommand.__init__(self, number=FileClose.NUMBER, \ - type=0x01, command=_id) - -class FileCreate(PathCommand): - """ Create a file """ - NUMBER = 0x1a #: Command number - def __init__(self, path): - PathCommand.__init__(self, path, FileCreate.NUMBER) - -class FileDelete(PathCommand): - """ Delete a file """ - NUMBER = 0x1B - def __init__(self, path): - PathCommand.__init__(self, path, FileDelete.NUMBER) - -class DirDelete(PathCommand): - """ Delete a directory """ - NUMBER = 0x31 - def __init__(self, path): - PathCommand.__init__(self, path, DirDelete.NUMBER) - -class FileOpen(PathCommand): - """ File open command """ - NUMBER = 0x10 #: Command number - READ = 0x00 #: Open file in read mode - WRITE = 0x01 #: Open file in write mode - path_length = field(start=20, fmt=DWORD) - path = stringfield(path_length, start=24) - - def __init__(self, path, mode=0x00): - PathCommand.__init__(self, path, FileOpen.NUMBER, path_len_at_byte=20) - self.mode = mode - - @dynamic_property - def mode(self): - doc = \ - """ - The file open mode. Is either L{FileOpen.READ} - or L{FileOpen.WRITE}. C{unsigned int} stored at byte 16. - """ - def fget(self): - return self.unpack(start=16, fmt=DWORD)[0] - - def fset(self, val): - self.pack(val, start=16, fmt=DWORD) - - return property(doc=doc, fget=fget, fset=fset) - - -class FileIO(Command): - """ Command to read/write from an open file """ - RNUMBER = 0x16 #: Command number to read from a file - WNUMBER = 0x17 #: Command number to write to a file - id = field(start=16, fmt=DWORD) #: The file ID returned by a FileOpen command - offset = field(start=20, fmt=DDWORD) #: offset in the file at which to read - size = field(start=28, fmt=DWORD) #: The number of bytes to reead from file. - def __init__(self, _id, offset, size, mode=0x16): - """ - @param _id: File identifier returned by a L{FileOpen} command - @type id: C{unsigned int} - @param offset: Position in file at which to read - @type offset: C{unsigned long long} - @param size: number of bytes to read - @type size: C{unsigned int} - @param mode: Either L{FileIO.RNUMBER} or L{File.WNUMBER} - """ - Command.__init__(self, 32) - self.number = mode - self.type = 0x01 - self.length = 16 - self.id = _id - self.offset = offset - self.size = size - - -class PathQuery(PathCommand): - """ Defines structure of command that requests information about a path """ - NUMBER = 0x18 #: Command number - def __init__(self, path): - PathCommand.__init__(self, path, PathQuery.NUMBER) - -class SetFileInfo(PathCommand): - """ Set File information """ - NUMBER = 0x19 #: Command number - def __init__(self, path): - PathCommand.__init__(self, path, SetFileInfo.NUMBER) - -class Response(Command): - """ - Defines the structure of response packets received from the device. - - C{Response} inherits from C{Command} as the - first 16 bytes have the same structure. - """ - - SIZE = 32 #: Size of response packets in the SONY protocol - # Response number, the command number of a command - # packet sent sometime before this packet was received - rnumber = field(start=16, fmt=DWORD) - # Used to indicate error conditions. A value of 0 means - # there was no error - code = field(start=20, fmt=DWORD) - # Used to indicate the size of the next bulk read - data_size = field(start=28, fmt=DWORD) - - def __init__(self, packet): - """ C{len(packet) == Response.SIZE} """ - if len(packet) != Response.SIZE: - raise PacketError(str(self.__class__)[7:-2] + \ - " packets must have exactly " + \ - str(Response.SIZE) + " bytes not " + str(len(packet))) - Command.__init__(self, packet) - if self.number != 0x00001000: - raise PacketError("Response packets must have their number set to " \ - + hex(0x00001000)) - - @dynamic_property - def data(self): - doc = \ - """ - The last 3 DWORDs (12 bytes) of data in this - response packet. Returned as a list of unsigned integers. - """ - def fget(self): - return self.unpack(start=20, fmt="=} C{16} """ - if "__len__" in dir(packet): - if len(packet) < 16 : - raise PacketError(str(self.__class__)[7:-2] + \ - " packets must have a length of atleast 16 bytes. "\ - "Got initializer of " + str(len(packet)) + " bytes.") - elif packet < 16: - raise PacketError(str(self.__class__)[7:-2] + \ - " packets must have a length of atleast 16 bytes") - TransferBuffer.__init__(self, packet) - - -class FileProperties(Answer): - - """ - Defines the structure of packets that contain size, date and - permissions information about files/directories. - """ - - file_size = field(start=16, fmt=DDWORD) #: Size in bytes of the file - file_type = field(start=24, fmt=DWORD) #: 1 == file, 2 == dir - ctime = field(start=28, fmt=DWORD) #: Creation time as an epoch - wtime = field(start=32, fmt=DWORD) #: Modification time as an epoch - # 0 = default permissions, 4 = read only - permissions = field(start=36, fmt=DWORD) - - @dynamic_property - def is_dir(self): - doc = """True if path points to a directory, False if it points to a file.""" - - def fget(self): - return (self.file_type == 2) - - def fset(self, val): - if val: - val = 2 - else: - val = 1 - self.file_type = val - - return property(doc=doc, fget=fget, fset=fset) - - - @dynamic_property - def is_readonly(self): - doc = """ Whether this file is readonly.""" - - def fget(self): - return self.unpack(start=36, fmt=DWORD)[0] != 0 - - def fset(self, val): - if val: - val = 4 - else: - val = 0 - self.pack(val, start=36, fmt=DWORD) - - return property(doc=doc, fget=fget, fset=fset) - - -class USBProtocolVersion(Answer): - """ Get USB Protocol version """ - version = field(start=16, fmt=DDWORD) - -class IdAnswer(Answer): - - """ Defines the structure of packets that contain identifiers for queries. """ - - @dynamic_property - def id(self): - doc = \ - """ - The identifier. C{unsigned int} stored in 4 bytes - at byte 16. Should be sent in commands asking - for the next item in the list. - """ - - def fget(self): - return self.unpack(start=16, fmt=DWORD)[0] - - def fset(self, val): - self.pack(val, start=16, fmt=DWORD) - - return property(doc=doc, fget=fget, fset=fset) - -class DeviceInfo(Answer): - """ Defines the structure of the packet containing information about the device """ - device_name = field(start=16, fmt="<32s") - device_version = field(start=48, fmt="<32s") - software_version = field(start=80, fmt="<24s") - mime_type = field(start=104, fmt="<32s") - - -class TotalSpaceAnswer(Answer): - total = field(start=24, fmt=DDWORD) #: Total space available - # Supposedly free space available, but it does not work for main memory - free_space = field(start=32, fmt=DDWORD) - -class FreeSpaceAnswer(Answer): - SIZE = 24 - free = field(start=16, fmt=DDWORD) - - -class ListAnswer(Answer): - """ Defines the structure of packets that contain items in a list. """ - name_length = field(start=20, fmt=DWORD) - name = stringfield(name_length, start=24) - - @dynamic_property - def is_dir(self): - doc = \ - """ - True if list item points to a directory, False if it points to a file. - C{unsigned int} stored in 4 bytes at byte 16. - """ - - def fget(self): - return (self.unpack(start=16, fmt=DWORD)[0] == 2) - - def fset(self, val): - if val: val = 2 - else: val = 1 - self.pack(val, start=16, fmt=DWORD) - - return property(doc=doc, fget=fget, fset=fset) - diff --git a/src/calibre/ebooks/lrf/meta.py b/src/calibre/ebooks/lrf/meta.py index 52809b0775..822257d457 100644 --- a/src/calibre/ebooks/lrf/meta.py +++ b/src/calibre/ebooks/lrf/meta.py @@ -18,7 +18,6 @@ from cStringIO import StringIO import xml.dom.minidom as dom from functools import wraps -from calibre.devices.prs500.prstypes import field from calibre.ebooks.metadata import MetaInformation, string_to_authors BYTE = "}, that implements access + to protocol packets in a human readable way. + """ + def __init__(self, start=16, fmt=DWORD): + """ + @param start: The byte at which this field is stored in the buffer + @param fmt: The packing format for this field. + See U{struct}. + """ + self._fmt, self._start = fmt, start + + def __get__(self, obj, typ=None): + return obj.unpack(start=self._start, fmt=self._fmt)[0] + + def __set__(self, obj, val): + obj.pack(val, start=self._start, fmt=self._fmt) + + def __repr__(self): + typ = "" + if self._fmt == DWORD: + typ = "unsigned int" + if self._fmt == QWORD: + typ = "unsigned long long" + return "An " + typ + " stored in " + \ + str(struct.calcsize(self._fmt)) + \ + " bytes starting at byte " + str(self._start) + + class versioned_field(field): def __init__(self, vfield, version, start=0, fmt=WORD): field.__init__(self, start=start, fmt=fmt) diff --git a/src/calibre/linux.py b/src/calibre/linux.py index 2613c168fd..1a66324d9f 100644 --- a/src/calibre/linux.py +++ b/src/calibre/linux.py @@ -16,7 +16,7 @@ from calibre import CurrentDir entry_points = { 'console_scripts': [ \ - 'ebook-device = calibre.devices.prs500.cli.main:main', + 'ebook-device = calibre.devices.cli:main', 'ebook-meta = calibre.ebooks.metadata.cli:main', 'ebook-convert = calibre.ebooks.conversion.cli:main', 'markdown-calibre = calibre.ebooks.markdown.markdown:main', @@ -299,7 +299,7 @@ class PostInstall: return 0 ;; cp ) - if [[ ${cur} == prs500:* ]]; then + if [[ ${cur} == dev:* ]]; then COMPREPLY=( $(_ebook_device_ls "${cur:7}") ) return 0 else @@ -307,20 +307,20 @@ class PostInstall: return 0 fi ;; - prs500 ) + dev ) COMPREPLY=( $(compgen -W "cp ls rm mkdir touch cat info books df" "${cur}") ) return 0 ;; * ) - if [[ ${cur} == prs500:* ]]; then + if [[ ${cur} == dev:* ]]; then COMPREPLY=( $(_ebook_device_ls "${cur:7}") ) return 0 else - if [[ ${prev} == prs500:* ]]; then + if [[ ${prev} == dev:* ]]; then _filedir return 0 else - COMPREPLY=( $(compgen -W "prs500:" "${cur}") ) + COMPREPLY=( $(compgen -W "dev:" "${cur}") ) return 0 fi return 0 diff --git a/src/calibre/trac/setup.py b/src/calibre/trac/setup.py deleted file mode 100644 index ae15f732d0..0000000000 --- a/src/calibre/trac/setup.py +++ /dev/null @@ -1,21 +0,0 @@ -__license__ = 'GPL v3' -__copyright__ = '2008, Kovid Goyal ' - -from setuptools import find_packages, setup - -# name can be any name. This name will be used to create .egg file. -# name that is used in packages is the one that is used in the trac.ini file. -# use package name as entry_points -setup( - name='TracLibprs500Plugins', version='0.1', - packages=find_packages(exclude=['*.tests*']), - entry_points = """ - [trac.plugins] - download = plugins.download - changelog = plugins.Changelog - """, - package_data={'plugins': ['templates/*.html', - 'htdocs/css/*.css', - 'htdocs/images/*']}, -) -