diff --git a/src/calibre/ebooks/epub/__init__.py b/src/calibre/ebooks/epub/__init__.py index a64dd43ac3..7bd6eeab50 100644 --- a/src/calibre/ebooks/epub/__init__.py +++ b/src/calibre/ebooks/epub/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +from __future__ import with_statement __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' __docformat__ = 'restructuredtext en' @@ -6,10 +6,12 @@ __docformat__ = 'restructuredtext en' ''' Conversion to EPUB. ''' -import sys, textwrap, re +import sys, textwrap, re, os, uuid +from itertools import cycle from calibre.utils.config import Config, StringConfig from calibre.utils.zipfile import ZipFile, ZIP_STORED from calibre.ebooks.html import config as common_config, tostring +from lxml import etree class DefaultProfile(object): @@ -36,6 +38,38 @@ def rules(stylesheets): if r.type == r.STYLE_RULE: yield r +def decrypt_font(key, path): + raw = open(path, 'rb').read() + crypt = raw[:1024] + key = cycle(iter(key)) + decrypt = ''.join([chr(ord(x)^key.next()) for x in crypt]) + with open(path, 'wb') as f: + f.write(decrypt) + f.write(raw[1024:]) + +def process_encryption(encfile, opf): + key = None + m = re.search(r'(?i)(urn:uuid:[0-9a-f-]+)', open(opf, 'rb').read()) + if m: + key = m.group(1) + key = list(map(ord, uuid.UUID(key).bytes)) + try: + root = etree.parse(encfile) + for em in root.xpath('descendant::*[contains(name(), "EncryptionMethod")]'): + algorithm = em.get('Algorithm', '') + if algorithm != 'http://ns.adobe.com/pdf/enc#RC': + return False + cr = em.getparent().xpath('descendant::*[contains(name(), "CipherReference")]')[0] + uri = cr.get('URI') + path = os.path.abspath(os.path.join(os.path.dirname(encfile), '..', *uri.split('/'))) + if os.path.exists(path): + decrypt_font(key, path) + return True + except: + import traceback + traceback.print_exc() + return False + def initialize_container(path_to_container, opf_name='metadata.opf'): ''' Create an empty EPUB document, with a default skeleton. diff --git a/src/calibre/ebooks/epub/from_any.py b/src/calibre/ebooks/epub/from_any.py index d86174f514..b5c1f48937 100644 --- a/src/calibre/ebooks/epub/from_any.py +++ b/src/calibre/ebooks/epub/from_any.py @@ -12,7 +12,7 @@ from contextlib import nested from calibre import extract, walk from calibre.ebooks import DRMError -from calibre.ebooks.epub import config as common_config +from calibre.ebooks.epub import config as common_config, process_encryption from calibre.ebooks.epub.from_html import convert as html2epub, find_html_index from calibre.ptempfile import TemporaryDirectory from calibre.ebooks.metadata import MetaInformation @@ -72,12 +72,19 @@ def epub2opf(path, tdir, opts): zf = ZipFile(path) zf.extractall(tdir) opts.chapter_mark = 'none' - if os.path.exists(os.path.join(tdir, 'META-INF', 'encryption.xml')): - raise DRMError(os.path.basename(path)) + encfile = os.path.join(tdir, 'META-INF', 'encryption.xml') + opf = None for f in walk(tdir): if f.lower().endswith('.opf'): - return f - raise ValueError('%s is not a valid EPUB file'%path) + opf = f + break + if opf and os.path.exists(encfile): + if not process_encryption(encfile, opf): + raise DRMError(os.path.basename(path)) + + if opf is None: + raise ValueError('%s is not a valid EPUB file'%path) + return opf def odt2epub(path, tdir, opts): from calibre.ebooks.odt.to_oeb import Extract diff --git a/src/calibre/ebooks/epub/iterator.py b/src/calibre/ebooks/epub/iterator.py index 4dd7fe1cc8..127af6e55f 100644 --- a/src/calibre/ebooks/epub/iterator.py +++ b/src/calibre/ebooks/epub/iterator.py @@ -37,14 +37,16 @@ class UnsupportedFormatError(Exception): class SpineItem(unicode): - def __init__(self, path): - unicode.__init__(self, path) + def __new__(cls, *args): + obj = super(SpineItem, cls).__new__(cls, *args) + path = args[0] raw = open(path, 'rb').read() - raw, self.encoding = xml_to_unicode(raw) - self.character_count = character_count(raw) - self.start_page = -1 - self.pages = -1 - self.max_page = -1 + raw, obj.encoding = xml_to_unicode(raw) + obj.character_count = character_count(raw) + obj.start_page = -1 + obj.pages = -1 + obj.max_page = -1 + return obj def html2opf(path, tdir, opts): opts = copy.copy(opts)