diff --git a/calibre-plugin/__init__.py b/calibre-plugin/__init__.py index a94f553..0db766d 100644 --- a/calibre-plugin/__init__.py +++ b/calibre-plugin/__init__.py @@ -17,14 +17,15 @@ import subprocess from calibre.utils.config import config_dir # type: ignore from calibre.constants import iswindows, isosx # type: ignore +from calibre.gui2 import (question_dialog, error_dialog, info_dialog, choose_save_file) # type: ignore class DeACSM(FileTypePlugin): name = PLUGIN_NAME - description = "Takes an Adobe ACSM file and converts that into a useable EPUB file" + description = "Takes an Adobe ACSM file and converts that into a useable EPUB file." supported_platforms = ['linux'] - author = "Leseratte10" + author = "Leseratte10 (Plugin), Grégory Soutadé (libgourou)" version = PLUGIN_VERSION_TUPLE minimum_calibre_version = (5, 0, 0) file_types = set(['acsm']) @@ -34,14 +35,7 @@ class DeACSM(FileTypePlugin): def initialize(self): """ - Dynamic modules can't be imported/loaded from a zipfile. - So this routine will extract the appropriate - library for the target OS and copy it to the 'alfcrypto' subdirectory of - calibre's configuration directory. That 'alfcrypto' directory is then - inserted into the syspath (as the very first entry) in the run function - so the CDLL stuff will work in the alfcrypto.py script. - The extraction only happens once per version of the plugin - Also perform upgrade of preferences once per version + On initialization, make sure the libgourou code is present for compilation. """ try: self.pluginsdir = os.path.join(config_dir,"plugins") @@ -127,11 +121,15 @@ class DeACSM(FileTypePlugin): print(ret) + if not (os.path.exists(outputname)): + error_dialog(None, "ACSM->EPUB failed", "Could not convert ACSM to EPUB:", det_msg=str(ret), show=True, show_copy_button=True) + print("{0} v{1}: Failed, return original ...".format(PLUGIN_NAME, PLUGIN_VERSION)) + return path_to_ebook + + return outputname - print("{0} v{1}: Failed, return original ...".format(PLUGIN_NAME, PLUGIN_VERSION)) - return path_to_ebook - + diff --git a/calibre-plugin/config.py b/calibre-plugin/config.py index 722c10d..4db6b60 100644 --- a/calibre-plugin/config.py +++ b/calibre-plugin/config.py @@ -3,11 +3,14 @@ # pyright: reportUndefinedVariable=false -import os, glob, shutil, tarfile, subprocess, time +import os, glob, shutil, tarfile, subprocess, time, tempfile, datetime + +from lxml import etree + from PyQt5.Qt import (Qt, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QLineEdit, - QGroupBox, QPushButton, QListWidget, QListWidgetItem, - QAbstractItemView, QIcon, QDialog, QDialogButtonBox, QUrl) + QGroupBox, QPushButton, QListWidget, QListWidgetItem, QInputDialog, + QLineEdit, QAbstractItemView, QIcon, QDialog, QDialogButtonBox, QUrl) from PyQt5 import Qt as QtGui from zipfile import ZipFile @@ -46,24 +49,168 @@ class ConfigWidget(QWidget): layout.addWidget(self.button_compile) - ua_group_box = QGroupBox(_('Path to account:'), self) + ua_group_box = QGroupBox(_('Account information:'), self) layout.addWidget(ua_group_box) ua_group_box_layout = QVBoxLayout() ua_group_box.setLayout(ua_group_box_layout) - self.txtboxUA = QtGui.QLineEdit(self) - self.txtboxUA.setToolTip(_("Enter folder path to account data")) - self.txtboxUA.setText(self.tempdeacsmprefs['path_to_account_data']) - ua_group_box_layout.addWidget(self.txtboxUA) + #self.txtboxUA = QtGui.QLineEdit(self) + #self.txtboxUA.setToolTip(_("Enter folder path to account data")) + #self.txtboxUA.setText(self.tempdeacsmprefs['path_to_account_data']) + #ua_group_box_layout.addWidget(self.txtboxUA) + + info_string, activated = self.get_account_info() + + self.lblAccInfo = QtGui.QLabel(self) + self.lblAccInfo.setText(info_string) + ua_group_box_layout.addWidget(self.lblAccInfo) + + if not activated: + self.button_link_account = QtGui.QPushButton(self) + self.button_link_account.setText(_("Link to ADE account")) + self.button_link_account.clicked.connect(self.link_account) + ua_group_box_layout.addWidget(self.button_link_account) self.button_export_key = QtGui.QPushButton(self) - self.button_export_key.setText(_("Export account key")) + self.button_export_key.setText(_("Export account encryption key")) self.button_export_key.clicked.connect(self.export_key) + self.button_export_key.setEnabled(activated) ua_group_box_layout.addWidget(self.button_export_key) + self.button_export_activation = QtGui.QPushButton(self) + self.button_export_activation.setText(_("Export account activation data")) + self.button_export_activation.clicked.connect(self.export_activation) + self.button_export_activation.setEnabled(activated) + ua_group_box_layout.addWidget(self.button_export_activation) + self.resize(self.sizeHint()) + def get_account_info(self): + + activation_xml_path = os.path.join(self.deacsmprefs["path_to_account_data"], "activation.xml") + device_xml_path = os.path.join(self.deacsmprefs["path_to_account_data"], "device.xml") + + container = None + try: + container = etree.parse(activation_xml_path) + containerdev = etree.parse(device_xml_path) + except (FileNotFoundError, OSError) as e: + return "Not authorized for any ADE ID", False + + try: + adeptNS = lambda tag: '{%s}%s' % ('http://ns.adobe.com/adept', tag) + usernameXML = container.find(adeptNS("credentials")).find(adeptNS("username")) + devicenameXML = containerdev.find(adeptNS("deviceName")) + ade_type = usernameXML.get('method', "unknown") + ade_mail = usernameXML.text + ade_device_name = devicenameXML.text + return "Authorized with ADE ID ("+ade_type+") " + ade_mail + "\non device " + ade_device_name, True + except: + return "ADE authorization seems to be corrupted", False + + + def export_activation(self): + pluginsdir = os.path.join(config_dir,"plugins") + maindir = os.path.join(pluginsdir,"DeACSM") + + filters = [("ZIP", ["zip"])] + filename = choose_save_file(self, "Export ADE activation files", _("Export ADE activation files"), filters, all_files=False) + + print("would export to " + filename) + + try: + with ZipFile(filename, 'w') as zipfile: + zipfile.write(os.path.join(self.deacsmprefs["path_to_account_data"], "device.xml"), "device.xml") + zipfile.write(os.path.join(self.deacsmprefs["path_to_account_data"], "activation.xml"), "activation.xml") + zipfile.write(os.path.join(self.deacsmprefs["path_to_account_data"], "devicesalt"), "devicesalt") + except: + return error_dialog(None, "Export failed", "Export failed.", show=True, show_copy_button=False) + + + + def link_account(self): + pluginsdir = os.path.join(config_dir,"plugins") + maindir = os.path.join(pluginsdir,"DeACSM") + verdir = os.path.join(maindir,PLUGIN_VERSION) + + mail, ok = QInputDialog.getText(self, "Authorizing ADE account", "Please enter mail address") + passwd, ok = QInputDialog.getText(self, "Authorizing ADE account", "Please enter password", QLineEdit.Password) + + + import calibre_plugins.deacsm.prefs as prefs # type: ignore + deacsmprefs = prefs.DeACSM_Prefs() + + output_dir = tempfile.mkdtemp() + + my_env = os.environ.copy() + my_env["LD_LIBRARY_PATH"] = ".:" + my_env["LD_LIBRARY_PATH"] + + # Make backup ... + if (os.path.exists(os.path.join(deacsmprefs["path_to_account_data"], "device.xml")) or + os.path.exists(os.path.join(deacsmprefs["path_to_account_data"], "activation.xml")) or + os.path.exists(os.path.join(deacsmprefs["path_to_account_data"], "devicesalt")) ): + + try: + currenttime = datetime.datetime.now() + backup_file = "backup_" + str(currenttime.year) + "-" + str(currenttime.month) + "-" + str(currenttime.day) + "_" + backup_file += str(currenttime.hour) + "-" + str(currenttime.minute) + "-" + str(currenttime.second) + ".zip" + with ZipFile(os.path.join(deacsmprefs["path_to_account_data"], backup_file), 'w') as zipfile: + try: + zipfile.write(os.path.join(self.deacsmprefs["path_to_account_data"], "device.xml"), "device.xml") + except: + pass + try: + zipfile.write(os.path.join(self.deacsmprefs["path_to_account_data"], "activation.xml"), "activation.xml") + except: + pass + try: + zipfile.write(os.path.join(self.deacsmprefs["path_to_account_data"], "devicesalt"), "devicesalt") + except: + pass + except: + raise + + ret = None + + try: + + + ret = subprocess.run([os.path.join(verdir, "adept_activate"), + "-u", mail, + "-p", passwd, + "-O", output_dir, + "-v" + ], capture_output=True, shell=False, cwd=verdir, env=my_env) + + print(ret) + + except: + return error_dialog(None, "ADE activation failed", "ADE activation failed", det_msg=str(ret), show=True, show_copy_button=True) + + + try: + shutil.copy(os.path.join(output_dir, "device.xml"), os.path.join(deacsmprefs["path_to_account_data"], "device.xml")) + shutil.copy(os.path.join(output_dir, "activation.xml"), os.path.join(deacsmprefs["path_to_account_data"], "activation.xml")) + shutil.copy(os.path.join(output_dir, "devicesalt"), os.path.join(deacsmprefs["path_to_account_data"], "devicesalt")) + shutil.rmtree(output_dir) + + info_dialog(None, "Done", "Authorization successful!", show=True, show_copy_button=False) + + except IndexError: + return error_dialog(None, "Authorization failed", "Authorization failed", show=True, det_msg=str(ret), show_copy_button=True) + + # update display + info_string, activated = self.get_account_info() + self.lblAccInfo.setText(info_string) + + self.button_link_account.setEnabled(False) + self.button_export_key.setEnabled(True) + self.button_export_activation.setEnabled(True) + + + + def export_key(self): pluginsdir = os.path.join(config_dir,"plugins") maindir = os.path.join(pluginsdir,"DeACSM") @@ -104,6 +251,8 @@ class ConfigWidget(QWidget): "-e" ], capture_output=True, shell=False, cwd=verdir, env=my_env) + print(ret) + except: return error_dialog(None, "Export failed", "Export failed.", det_msg=str(ret), show=True, show_copy_button=True) @@ -112,10 +261,10 @@ class ConfigWidget(QWidget): shutil.move(new_key, filename) info_dialog(None, "Done", "Key successfully exported", show=True, show_copy_button=False) except IndexError: - return error_dialog(None, "Export failed", "Export failed.", det_msg=str(ret), show=True, show_copy_button=True) + return error_dialog(None, "Export failed", "Export failed.", show=True, show_copy_button=True) + - print(ret) @@ -147,7 +296,7 @@ class ConfigWidget(QWidget): print(ret2) try: - shutil.copy(os.path.join(verdir, "libgourou", "AAlibgourou.so"), verdir) + shutil.copy(os.path.join(verdir, "libgourou", "libgourou.so"), verdir) shutil.copy(os.path.join(verdir, "libgourou", "utils", "acsmdownloader"), verdir) shutil.copy(os.path.join(verdir, "libgourou", "utils", "adept_activate"), verdir) info_dialog(None, "Done", "Compiling successful", show=True, show_copy_button=False) @@ -158,7 +307,7 @@ class ConfigWidget(QWidget): def save_settings(self): - self.deacsmprefs.set('path_to_account_data', self.txtboxUA.text()) + #self.deacsmprefs.set('path_to_account_data', self.txtboxUA.text()) self.deacsmprefs.writeprefs() def load_resource(self, name): diff --git a/calibre-plugin/prefs.py b/calibre-plugin/prefs.py index 1df84db..169ccc8 100644 --- a/calibre-plugin/prefs.py +++ b/calibre-plugin/prefs.py @@ -9,7 +9,6 @@ import traceback from calibre.utils.config import JSONConfig, config_dir # type: ignore from calibre_plugins.deacsm.__init__ import PLUGIN_NAME # type: ignore -from calibre.constants import isosx, islinux # type: ignore class DeACSM_Prefs():