From 51a61114d8aa09e2528230f8baae650e39dbb5b6 Mon Sep 17 00:00:00 2001 From: Florian Bach Date: Fri, 12 Nov 2021 19:15:30 +0100 Subject: [PATCH] v0.0.11: Include UUID in export filename, fix other FT plugins --- calibre-plugin/__init__.py | 87 ++++++++++++++++++++++++++----- calibre-plugin/config.py | 38 ++++++++++++-- calibre-plugin/libadobeAccount.py | 16 +++++- 3 files changed, 124 insertions(+), 17 deletions(-) diff --git a/calibre-plugin/__init__.py b/calibre-plugin/__init__.py index dfcc79f..a03f6d6 100644 --- a/calibre-plugin/__init__.py +++ b/calibre-plugin/__init__.py @@ -15,10 +15,13 @@ # v0.0.8: More PDF bugfixes, support unlimited PDF file sizes, tell Calibre ACSMs are books. # v0.0.9: Add FulfillmentNotification support, add LoanReturn support. # v0.0.10: Fix nonce calculation, merge PRs #3 and #4 (PyCryptodome stuff) +# v0.0.11: Ignore SSL errors during ACS notify, improve element hashing code, +# improve PassHash support, include UUID in key export filename, +# fix bug that would block other FileTypePlugins from calibre.customize import FileTypePlugin # type: ignore -__version__ = '0.0.10' +__version__ = '0.0.11' PLUGIN_NAME = "DeACSM" PLUGIN_VERSION_TUPLE = tuple([int(x) for x in __version__.split(".")]) @@ -304,26 +307,86 @@ class DeACSM(FileTypePlugin): # Got a file # Because Calibre still thinks this is an ACSM file (not an EPUB) - # it will not run other plugins like Alf / DeDRM. - # So we have to manually check if it's installed, - # and if it is, run it to remove DRM. + # it will not run other FileTypePlugins that handle EPUB (or PDF) files. + # Loop through all plugins (the list is already sorted by priority), + # then execute all of them that can handle EPUB / PDF. + try: from calibre.customize.ui import _initialized_plugins, is_disabled + from calibre.customize import FileTypePlugin + + original_file_for_plugins = rpl + + oo, oe = sys.stdout, sys.stderr + for plugin in _initialized_plugins: - if (plugin.name == "DeDRM" and not is_disabled(plugin)): - print("{0} v{1}: Executing DeDRM plugin ...".format(PLUGIN_NAME, PLUGIN_VERSION)) - return plugin.run(rpl) + + #print("{0} v{1}: Plugin '{2}' has prio {3}".format(PLUGIN_NAME, PLUGIN_VERSION, plugin.name, plugin.priority)) + + # Check if this is a FileTypePlugin + if not isinstance(plugin, FileTypePlugin): + #print("{0} v{1}: Plugin '{2}' is no FileTypePlugin, skipping ...".format(PLUGIN_NAME, PLUGIN_VERSION, plugin.name)) + continue + + # Check if it's disabled + if is_disabled(plugin): + #print("{0} v{1}: Plugin '{2}' is disabled, skipping ...".format(PLUGIN_NAME, PLUGIN_VERSION, plugin.name)) + continue + + if plugin.name == self.name: + #print("{0} v{1}: Plugin '{2}' is me - skipping".format(PLUGIN_NAME, PLUGIN_VERSION, plugin.name)) + continue + + # Check if it's supposed to run on import: + if not plugin.on_import: + #print("{0} v{1}: Plugin '{2}' isn't supposed to run during import, skipping ...".format(PLUGIN_NAME, PLUGIN_VERSION, plugin.name)) + continue + + # Check filetype + # If neither the book file extension nor "*" is in the plugin, + # don't execute it. + my_file_type = os.path.splitext(rpl)[-1].lower().replace('.', '') + if (not my_file_type in plugin.file_types): + #print("{0} v{1}: Plugin '{2}' doesn't support {3} files, skipping ...".format(PLUGIN_NAME, PLUGIN_VERSION, plugin.name, my_file_type)) + continue + + if ("acsm" in plugin.file_types or "*" in plugin.file_types): + #print("{0} v{1}: Plugin '{2}' would run anyways, skipping ...".format(PLUGIN_NAME, PLUGIN_VERSION, plugin.name, my_file_type)) + continue + + print("{0} v{1}: Executing plugin {2} ...".format(PLUGIN_NAME, PLUGIN_VERSION, plugin.name)) + + plugin.original_path_to_file = original_file_for_plugins + + try: + plugin_ret = None + plugin_ret = plugin.run(rpl) + except: + print("{0} v{1}: Running file type plugin failed with traceback:".format(PLUGIN_NAME, PLUGIN_VERSION)) + traceback.print_exc(file=oe) + + # Restore stdout and stderr, in case a plugin broke them. + sys.stdout, sys.stderr = oo, oe + + + if plugin_ret is not None: + # If the plugin returned a new path, update that. + print("{0} v{1}: Plugin returned path '{2}', updating.".format(PLUGIN_NAME, PLUGIN_VERSION, plugin_ret)) + rpl = plugin_ret + else: + print("{0} v{1}: Plugin returned nothing - skipping".format(PLUGIN_NAME, PLUGIN_VERSION)) + + + except: - print("{0} v{1}: Error while checking for DeDRM plugin.".format(PLUGIN_NAME, PLUGIN_VERSION)) + print("{0} v{1}: Error while executing other plugins".format(PLUGIN_NAME, PLUGIN_VERSION)) + traceback.print_exc() pass - # Looks like DeDRM is not installed, return book with DRM. + # Return path - either the original one or the one modified by the other plugins. return rpl return path_to_ebook - - - diff --git a/calibre-plugin/config.py b/calibre-plugin/config.py index 9744bce..dee5060 100644 --- a/calibre-plugin/config.py +++ b/calibre-plugin/config.py @@ -149,9 +149,31 @@ class ConfigWidget(QWidget): def export_activation(self): + try: + from calibre_plugins.deacsm.libadobe import update_account_path + from calibre_plugins.deacsm.libadobeAccount import getAccountUUID + except: + try: + from libadobe import update_account_path + from libadobeAccount import getAccountUUID + except: + print("{0} v{1}: Error while importing Account stuff".format(PLUGIN_NAME, PLUGIN_VERSION)) + traceback.print_exc() + + + update_account_path(self.deacsmprefs["path_to_account_data"]) + + account_uuid = None + export_filename = "adobe_account_backup.zip" + try: + account_uuid = getAccountUUID() + export_filename = "adobe_account_backup_uuid_" + account_uuid + ".zip" + except: + pass + filters = [("ZIP", ["zip"])] filename = choose_save_file(self, "Export ADE activation files", _("Export ADE activation files"), - filters, all_files=False, initial_filename="adobe_account_backup.zip") + filters, all_files=False, initial_filename=export_filename) if (filename is None): return @@ -283,11 +305,11 @@ class ConfigWidget(QWidget): try: from calibre_plugins.deacsm.libadobe import update_account_path - from calibre_plugins.deacsm.libadobeAccount import exportAccountEncryptionKeyDER + from calibre_plugins.deacsm.libadobeAccount import exportAccountEncryptionKeyDER, getAccountUUID except: try: from libadobe import update_account_path - from libadobeAccount import exportAccountEncryptionKeyDER + from libadobeAccount import exportAccountEncryptionKeyDER, getAccountUUID except: print("{0} v{1}: Error while importing Account stuff".format(PLUGIN_NAME, PLUGIN_VERSION)) traceback.print_exc() @@ -297,8 +319,16 @@ class ConfigWidget(QWidget): filters = [("DER Files", ["der"])] + account_uuid = None + export_filename = "adobe_encryption_key.der" + try: + account_uuid = getAccountUUID() + export_filename = "adobe_uuid_" + account_uuid + ".der" + except: + pass + filename = choose_save_file(self, "Export ADE keys", _("Export ADE keys"), filters, - all_files=False, initial_filename="adobe_encryption_key.der") + all_files=False, initial_filename=export_filename) if (filename is None): return diff --git a/calibre-plugin/libadobeAccount.py b/calibre-plugin/libadobeAccount.py index 8ef7eda..33c2c45 100644 --- a/calibre-plugin/libadobeAccount.py +++ b/calibre-plugin/libadobeAccount.py @@ -377,7 +377,21 @@ def activateDevice(): f.write("\n") f.close() - return True, ret + return True, ret + +def getAccountUUID(): + try: + activationxml = etree.parse(get_activation_xml_path()) + adNS = lambda tag: '{%s}%s' % ('http://ns.adobe.com/adept', tag) + user_uuid = activationxml.find("./%s/%s" % (adNS("credentials"), adNS("user"))).text + + if not user_uuid.startswith("urn:uuid:"): + return None + + return user_uuid[9:] + except: + return None + def exportAccountEncryptionKeyDER(output_file: str): try: