DeDRM_tools/Calibre_Plugins/ineptepub_plugin/__init__.py

214 lines
9.9 KiB
Python
Raw Normal View History

2012-11-07 19:14:25 +06:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
2012-11-07 19:14:25 +06:00
from __future__ import with_statement
__license__ = 'GPL v3'
__docformat__ = 'restructuredtext en'
2010-10-19 02:06:58 +06:00
# Released under the terms of the GNU General Public Licence, version 3 or
# later. <http://www.gnu.org/licenses/>
#
# Requires Calibre version 0.7.55 or higher.
2010-10-19 02:06:58 +06:00
#
# All credit given to i♥cabbages for the original standalone scripts.
# I had the much easier job of converting them to a calibre plugin.
2010-10-19 02:06:58 +06:00
#
# This plugin is meant to decrypt Adobe Digital Edition Epubs that are protected
# with Adobe's Adept encryption. It is meant to function without having to install
# any dependencies... other than having calibre installed, of course. It will still
2010-10-19 02:06:58 +06:00
# work if you have Python and PyCrypto already installed, but they aren't necessary.
#
# Configuration:
# When first run, the plugin will attempt to find your Adobe Digital Editions installation
# (on Windows and Mac OS's). If successful, it will create one or more
# 'calibre-adeptkey<n>.der' files and save them in calibre's configuration directory.
# It will use those files on subsequent runs. If there is already a 'calibre-adeptkey*.der'
# file in the directory, the plugin won't attempt to find the ADE installation.
# So if you have ADE installed on the same machine as calibre you are ready to go.
2010-10-19 02:06:58 +06:00
#
# If you already have keyfiles generated with i♥cabbages' ineptkey.pyw script,
2010-10-19 02:06:58 +06:00
# you can put those keyfiles in Calibre's configuration directory. The easiest
# way to find the correct directory is to go to Calibre's Preferences page... click
# on the 'Miscellaneous' button (looks like a gear), and then click the 'Open Calibre
# configuration directory' button. Copy your keyfiles in there. Just make sure that
2010-10-19 02:06:58 +06:00
# they have different names and are saved with the '.der' extension (like the ineptkey
# script produces). This directory isn't touched when upgrading Calibre, so it's quite
# safe to leave them there.
#
# Since there is no Linux version of Adobe Digital Editions, Linux users will have to
# obtain a keyfile through other methods and put the file in Calibre's configuration directory.
#
# All keyfiles with a '.der' extension found in Calibre's configuration directory will
# be used to attempt to decrypt a book.
#
# ** NOTE ** There is no plugin customization data for the Inept Epub DeDRM plugin.
#
# Revision history:
# 0.1 - Initial release
2010-11-12 04:11:36 +06:00
# 0.1.1 - Allow Windows users to make use of openssl if they have it installed.
2010-12-16 03:21:51 +06:00
# - Incorporated SomeUpdates zipfix routine.
# 0.1.2 - Removed Carbon dependency for Mac users. Fixes an issue that was a
# result of Calibre changing to python 2.7.
# 0.1.3 - bug fix for epubs with non-ascii chars in file names
2011-01-06 13:10:38 +06:00
# 0.1.4 - default to try PyCrypto first on Windows
2011-02-08 23:21:51 +06:00
# 0.1.5 - update zipfix to handle out of position mimetypes
# 0.1.6 - update zipfix to handle completely missing mimetype files
# 0.1.7 - update to new calibre plugin interface
2012-11-07 19:14:25 +06:00
# 0.1.8 - Fix for potential problem with PyCrypto
# 0.1.9 - Fix for potential problem with ADE keys and fix possible output/unicode problem
# 0.2.0 - Major code change to use unaltered ineptepub.py file 5.8 or later.
2010-10-19 02:06:58 +06:00
PLUGIN_NAME = u"Inept Epub DeDRM"
PLUGIN_VERSION_TUPLE = (0, 2, 0)
PLUGIN_VERSION = u'.'.join([str(x) for x in PLUGIN_VERSION_TUPLE])
2010-10-19 02:06:58 +06:00
import sys, os, re
2010-10-19 02:06:58 +06:00
class ADEPTError(Exception):
pass
from calibre.customize import FileTypePlugin
from calibre.constants import iswindows, isosx
2010-10-19 02:06:58 +06:00
# Wrap a stream so that output gets flushed immediately
# and also make sure that any unicode strings get
# encoded using "replace" before writing them.
class SafeUnbuffered:
def __init__(self, stream):
self.stream = stream
self.encoding = stream.encoding
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
if isinstance(data,unicode):
data = data.encode(self.encoding,"replace")
self.stream.write(data)
self.stream.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
2010-10-19 02:06:58 +06:00
class IneptDeDRM(FileTypePlugin):
2012-11-07 19:14:25 +06:00
name = PLUGIN_NAME
description = u"Removes DRM from secure Adobe epub files. Credit given to i♥cabbages for the original stand-alone scripts."
2010-10-19 02:06:58 +06:00
supported_platforms = ['linux', 'osx', 'windows']
author = u"DiapDealer, Apprentice Alf and i♥cabbages"
2012-11-07 19:14:25 +06:00
version = PLUGIN_VERSION_TUPLE
minimum_calibre_version = (0, 7, 55) # Compiled python libraries cannot be imported in earlier versions.
2010-10-19 02:06:58 +06:00
file_types = set(['epub'])
on_import = True
priority = 100
2010-10-19 02:06:58 +06:00
def run(self, path_to_ebook):
2012-11-07 19:14:25 +06:00
# make sure any unicode output gets converted safely with 'replace'
sys.stdout=SafeUnbuffered(sys.stdout)
sys.stderr=SafeUnbuffered(sys.stderr)
print u"{0} v{1}: Trying to decrypt {2}.".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook))
# Create a TemporaryPersistent file to work with.
# Check original epub archive for zip errors.
from calibre_plugins.ineptepub import zipfix
inf = self.temporary_file(u".epub")
try:
print u"{0} v{1}: Verifying zip archive integrity.".format(PLUGIN_NAME, PLUGIN_VERSION)
fr = zipfix.fixZip(path_to_ebook, inf.name)
fr.fix()
except Exception, e:
print u"{0} v{1}: Error when checking zip archive.".format(PLUGIN_NAME, PLUGIN_VERSION)
raise Exception(e)
2010-10-19 02:06:58 +06:00
return
#check the book
from calibre_plugins.ineptepub import ineptepub
if not ineptepub.adeptBook(inf.name):
print u"{0} v{1}: {2} is not a secure Adobe Adept ePub.".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook))
# return the original file, so that no error message is generated in the GUI
return path_to_ebook
2010-10-19 02:06:58 +06:00
# Load any keyfiles (*.der) included Calibre's config directory.
userkeys = []
# Find Calibre's configuration directory.
# self.plugin_path is passed in unicode because we defined our name in unicode
2010-10-19 02:06:58 +06:00
confpath = os.path.split(os.path.split(self.plugin_path)[0])[0]
print u"{0} v{1}: Calibre configuration directory = {2}".format(PLUGIN_NAME, PLUGIN_VERSION, confpath)
2010-10-19 02:06:58 +06:00
files = os.listdir(confpath)
filefilter = re.compile(u"\.der$", re.IGNORECASE)
2010-10-19 02:06:58 +06:00
files = filter(filefilter.search, files)
2012-11-07 19:14:25 +06:00
foundDefault = False
2010-10-19 02:06:58 +06:00
if files:
try:
for filename in files:
if filename[:16] == u"calibre-adeptkey":
2012-11-07 19:14:25 +06:00
foundDefault = True
2010-10-19 02:06:58 +06:00
fpath = os.path.join(confpath, filename)
with open(fpath, 'rb') as f:
userkeys.append([f.read(), filename])
print u"{0} v{1}: Keyfile {2} found in config folder.".format(PLUGIN_NAME, PLUGIN_VERSION, filename)
2010-10-19 02:06:58 +06:00
except IOError:
print u"{0} v{1}: Error reading keyfiles from config directory.".format(PLUGIN_NAME, PLUGIN_VERSION)
2010-10-19 02:06:58 +06:00
pass
2012-11-07 19:14:25 +06:00
if not foundDefault:
2010-10-19 02:06:58 +06:00
# Try to find key from ADE install and save the key in
# Calibre's configuration directory for future use.
if iswindows or isosx:
#ignore annoying future warning from key generation
import warnings
warnings.filterwarnings('ignore', category=FutureWarning)
2010-10-19 02:06:58 +06:00
# ADE key retrieval script included in respective OS folder.
2012-11-07 19:14:25 +06:00
from calibre_plugins.ineptepub.ineptkey import retrieve_keys
2010-10-19 02:06:58 +06:00
try:
2012-11-07 19:14:25 +06:00
keys = retrieve_keys()
for i,key in enumerate(keys):
keyname = u"calibre-adeptkey{0:d}.der".format(i)
userkeys.append([key,keyname])
keypath = os.path.join(confpath, keyname)
2012-11-07 19:14:25 +06:00
open(keypath, 'wb').write(key)
print u"{0} v{1}: Created keyfile {2} from ADE install.".format(PLUGIN_NAME, PLUGIN_VERSION, keyname)
2010-10-19 02:06:58 +06:00
except:
print u"{0} v{1}: Couldn\'t Retrieve key from ADE install.".format(PLUGIN_NAME, PLUGIN_VERSION)
2012-11-07 19:14:25 +06:00
pass
2010-10-19 02:06:58 +06:00
if not userkeys:
# No user keys found... bail out.
raise ADEPTError(u"{0} v{1}: No keys found. Check keyfile(s)/ADE install".format(PLUGIN_NAME, PLUGIN_VERSION))
2010-10-19 02:06:58 +06:00
return
2010-10-19 02:06:58 +06:00
# Attempt to decrypt epub with each encryption key found.
for userkeyinfo in userkeys:
print u"{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, userkeyinfo[1])
of = self.temporary_file(u".epub")
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
result = ineptepub.decryptBook(userkeyinfo[0], inf.name, of.name)
2010-10-19 02:06:58 +06:00
# Ebook is not an Adobe Adept epub... do nothing and pass it on.
# This allows a non-encrypted epub to be imported without error messages.
if result == 1:
print u"{0} v{1}: {2} is not a secure Adobe Adept ePub.".format(PLUGIN_NAME, PLUGIN_VERSION,os.path.basename(path_to_ebook))
2010-10-19 02:06:58 +06:00
of.close()
return path_to_ebook
break
2010-10-19 02:06:58 +06:00
# Decryption was successful return the modified PersistentTemporary
# file to Calibre's import process.
if result == 0:
print u"{0} v{1}: Encryption successfully removed.".format(PLUGIN_NAME, PLUGIN_VERSION)
of.close()
2010-10-19 02:06:58 +06:00
return of.name
break
print u"{0} v{1}: Encryption key incorrect.".format(PLUGIN_NAME, PLUGIN_VERSION)
of.close
2010-10-19 02:06:58 +06:00
# Something went wrong with decryption.
# Import the original unmolested epub.
raise ADEPTError(u"{0} v{1}: Ultimately failed to decrypt".format(PLUGIN_NAME, PLUGIN_VERSION))
2010-10-19 02:06:58 +06:00
return