2020-09-27 12:54:49 +02:00
#!/usr/bin/env python3
2013-03-20 11:23:54 +01:00
# -*- coding: utf-8 -*-
__license__ = ' GPL v3 '
2020-09-27 12:54:49 +02:00
# Python 3, September 2020
2020-09-26 22:22:47 +02:00
2013-03-20 11:23:54 +01:00
# Standard Python modules.
2021-12-23 11:29:58 +01:00
import sys , os , traceback , json , codecs , base64
2013-03-20 11:23:54 +01:00
2020-09-27 12:54:49 +02:00
from PyQt5 . Qt import ( Qt , QWidget , QHBoxLayout , QVBoxLayout , QLabel , QLineEdit ,
2021-11-15 17:59:48 +01:00
QGroupBox , QPushButton , QListWidget , QListWidgetItem , QCheckBox ,
2021-11-15 14:30:32 +01:00
QAbstractItemView , QIcon , QDialog , QDialogButtonBox , QUrl ,
2021-12-23 11:29:58 +01:00
QCheckBox , QComboBox )
2020-09-26 22:22:47 +02:00
2020-09-27 12:54:49 +02:00
from PyQt5 import Qt as QtGui
2013-03-20 11:23:54 +01:00
from zipfile import ZipFile
# calibre modules and constants.
from calibre . gui2 import ( error_dialog , question_dialog , info_dialog , open_url ,
2015-03-07 22:18:50 +01:00
choose_dir , choose_files , choose_save_file )
2013-03-20 11:23:54 +01:00
from calibre . utils . config import dynamic , config_dir , JSONConfig
from calibre . constants import iswindows , isosx
# modules from this plugin's zipfile.
from calibre_plugins . dedrm . __init__ import PLUGIN_NAME , PLUGIN_VERSION
from calibre_plugins . dedrm . __init__ import RESOURCE_NAME as help_file_name
2013-04-05 18:44:48 +02:00
from calibre_plugins . dedrm . utilities import uStrCmp
2013-03-20 11:23:54 +01:00
2013-04-05 18:44:48 +02:00
import calibre_plugins . dedrm . prefs as prefs
2015-07-29 19:11:19 +02:00
import calibre_plugins . dedrm . androidkindlekey as androidkindlekey
2013-03-20 11:23:54 +01:00
2021-11-16 17:14:03 +01:00
def checkForDeACSMkeys ( ) :
try :
from calibre_plugins . deacsm . libadobeAccount import exportAccountEncryptionKeyDER , getAccountUUID
2021-12-20 21:10:21 +01:00
except :
# Looks like DeACSM is not installed.
return None , None
try :
2021-11-16 17:14:03 +01:00
from calibre . ptempfile import TemporaryFile
acc_uuid = getAccountUUID ( )
if acc_uuid is None :
return None , None
name = " DeACSM_uuid_ " + getAccountUUID ( )
# Unfortunately, the DeACSM plugin only has code to export to a file, not to return raw key bytes.
# Make a temporary file, have the plugin write to that, then read (& delete) that file.
with TemporaryFile ( suffix = ' .der ' ) as tmp_key_file :
export_result = exportAccountEncryptionKeyDER ( tmp_key_file )
if ( export_result is False ) :
return None , None
# Read key file
with open ( tmp_key_file , ' rb ' ) as keyfile :
new_key_value = keyfile . read ( )
return new_key_value , name
except :
traceback . print_exc ( )
return None , None
2013-03-20 11:23:54 +01:00
class ConfigWidget ( QWidget ) :
2013-04-05 18:44:48 +02:00
def __init__ ( self , plugin_path , alfdir ) :
2013-03-20 11:23:54 +01:00
QWidget . __init__ ( self )
self . plugin_path = plugin_path
2013-04-05 18:44:48 +02:00
self . alfdir = alfdir
2013-03-20 11:23:54 +01:00
2013-04-05 18:44:48 +02:00
# get the prefs
self . dedrmprefs = prefs . DeDRM_Prefs ( )
2013-03-20 11:23:54 +01:00
2013-04-05 18:44:48 +02:00
# make a local copy
2013-03-20 11:23:54 +01:00
self . tempdedrmprefs = { }
self . tempdedrmprefs [ ' bandnkeys ' ] = self . dedrmprefs [ ' bandnkeys ' ] . copy ( )
self . tempdedrmprefs [ ' adeptkeys ' ] = self . dedrmprefs [ ' adeptkeys ' ] . copy ( )
self . tempdedrmprefs [ ' ereaderkeys ' ] = self . dedrmprefs [ ' ereaderkeys ' ] . copy ( )
self . tempdedrmprefs [ ' kindlekeys ' ] = self . dedrmprefs [ ' kindlekeys ' ] . copy ( )
2015-07-29 19:11:19 +02:00
self . tempdedrmprefs [ ' androidkeys ' ] = self . dedrmprefs [ ' androidkeys ' ] . copy ( )
2013-03-20 11:23:54 +01:00
self . tempdedrmprefs [ ' pids ' ] = list ( self . dedrmprefs [ ' pids ' ] )
self . tempdedrmprefs [ ' serials ' ] = list ( self . dedrmprefs [ ' serials ' ] )
2013-04-05 18:44:48 +02:00
self . tempdedrmprefs [ ' adobewineprefix ' ] = self . dedrmprefs [ ' adobewineprefix ' ]
self . tempdedrmprefs [ ' kindlewineprefix ' ] = self . dedrmprefs [ ' kindlewineprefix ' ]
2021-11-15 17:59:48 +01:00
self . tempdedrmprefs [ ' deobfuscate_fonts ' ] = self . dedrmprefs [ ' deobfuscate_fonts ' ]
2021-11-17 16:17:30 +01:00
self . tempdedrmprefs [ ' remove_watermarks ' ] = self . dedrmprefs [ ' remove_watermarks ' ]
2021-11-17 21:53:24 +01:00
self . tempdedrmprefs [ ' lcp_passphrases ' ] = list ( self . dedrmprefs [ ' lcp_passphrases ' ] )
2013-03-20 11:23:54 +01:00
# Start Qt Gui dialog layout
layout = QVBoxLayout ( self )
self . setLayout ( layout )
help_layout = QHBoxLayout ( )
layout . addLayout ( help_layout )
# Add hyperlink to a help file at the right. We will replace the correct name when it is clicked.
help_label = QLabel ( ' <a href= " http://www.foo.com/ " >Plugin Help</a> ' , self )
help_label . setTextInteractionFlags ( Qt . LinksAccessibleByMouse | Qt . LinksAccessibleByKeyboard )
help_label . setAlignment ( Qt . AlignRight )
help_label . linkActivated . connect ( self . help_link_activated )
help_layout . addWidget ( help_label )
keys_group_box = QGroupBox ( _ ( ' Configuration: ' ) , self )
layout . addWidget ( keys_group_box )
keys_group_box_layout = QHBoxLayout ( )
keys_group_box . setLayout ( keys_group_box_layout )
button_layout = QVBoxLayout ( )
keys_group_box_layout . addLayout ( button_layout )
self . bandn_button = QtGui . QPushButton ( self )
2021-12-23 11:29:58 +01:00
self . bandn_button . setToolTip ( _ ( " Click to manage keys for ADE books with PassHash algorithm. <br/>Commonly used by Barnes and Noble " ) )
self . bandn_button . setText ( " ADE PassHash (B&&N) ebooks " )
2013-03-20 11:23:54 +01:00
self . bandn_button . clicked . connect ( self . bandn_keys )
2015-03-17 18:49:30 +01:00
self . kindle_android_button = QtGui . QPushButton ( self )
2020-09-27 12:54:49 +02:00
self . kindle_android_button . setToolTip ( _ ( " Click to manage keys for Kindle for Android ebooks " ) )
self . kindle_android_button . setText ( " Kindle for Android ebooks " )
2015-07-29 19:11:19 +02:00
self . kindle_android_button . clicked . connect ( self . kindle_android )
2013-03-20 11:23:54 +01:00
self . kindle_serial_button = QtGui . QPushButton ( self )
2020-09-27 12:54:49 +02:00
self . kindle_serial_button . setToolTip ( _ ( " Click to manage eInk Kindle serial numbers for Kindle ebooks " ) )
self . kindle_serial_button . setText ( " eInk Kindle ebooks " )
2013-03-20 11:23:54 +01:00
self . kindle_serial_button . clicked . connect ( self . kindle_serials )
self . kindle_key_button = QtGui . QPushButton ( self )
2020-09-27 12:54:49 +02:00
self . kindle_key_button . setToolTip ( _ ( " Click to manage keys for Kindle for Mac/PC ebooks " ) )
self . kindle_key_button . setText ( " Kindle for Mac/PC ebooks " )
2013-03-20 11:23:54 +01:00
self . kindle_key_button . clicked . connect ( self . kindle_keys )
self . adept_button = QtGui . QPushButton ( self )
2020-09-27 12:54:49 +02:00
self . adept_button . setToolTip ( _ ( " Click to manage keys for Adobe Digital Editions ebooks " ) )
self . adept_button . setText ( " Adobe Digital Editions ebooks " )
2013-03-20 11:23:54 +01:00
self . adept_button . clicked . connect ( self . adept_keys )
self . mobi_button = QtGui . QPushButton ( self )
2020-09-27 12:54:49 +02:00
self . mobi_button . setToolTip ( _ ( " Click to manage PIDs for Mobipocket ebooks " ) )
self . mobi_button . setText ( " Mobipocket ebooks " )
2013-03-20 11:23:54 +01:00
self . mobi_button . clicked . connect ( self . mobi_keys )
self . ereader_button = QtGui . QPushButton ( self )
2020-09-27 12:54:49 +02:00
self . ereader_button . setToolTip ( _ ( " Click to manage keys for eReader ebooks " ) )
self . ereader_button . setText ( " eReader ebooks " )
2013-03-20 11:23:54 +01:00
self . ereader_button . clicked . connect ( self . ereader_keys )
2021-11-17 21:53:24 +01:00
self . lcp_button = QtGui . QPushButton ( self )
self . lcp_button . setToolTip ( _ ( " Click to manage passphrases for Readium LCP ebooks " ) )
self . lcp_button . setText ( " Readium LCP ebooks " )
self . lcp_button . clicked . connect ( self . readium_lcp_keys )
2013-03-20 11:23:54 +01:00
button_layout . addWidget ( self . kindle_serial_button )
2015-03-17 18:49:30 +01:00
button_layout . addWidget ( self . kindle_android_button )
2013-03-20 11:23:54 +01:00
button_layout . addWidget ( self . bandn_button )
button_layout . addWidget ( self . mobi_button )
button_layout . addWidget ( self . ereader_button )
button_layout . addWidget ( self . adept_button )
button_layout . addWidget ( self . kindle_key_button )
2021-11-17 21:53:24 +01:00
button_layout . addWidget ( self . lcp_button )
2013-03-20 11:23:54 +01:00
2021-11-15 17:59:48 +01:00
self . chkFontObfuscation = QtGui . QCheckBox ( _ ( " Deobfuscate EPUB fonts " ) )
self . chkFontObfuscation . setToolTip ( " Deobfuscates fonts in EPUB files after DRM removal " )
self . chkFontObfuscation . setChecked ( self . tempdedrmprefs [ " deobfuscate_fonts " ] )
button_layout . addWidget ( self . chkFontObfuscation )
2021-11-17 16:17:30 +01:00
self . chkRemoveWatermarks = QtGui . QCheckBox ( _ ( " Remove watermarks " ) )
self . chkRemoveWatermarks . setToolTip ( " Tries to remove watermarks from files " )
self . chkRemoveWatermarks . setChecked ( self . tempdedrmprefs [ " remove_watermarks " ] )
button_layout . addWidget ( self . chkRemoveWatermarks )
2013-03-20 11:23:54 +01:00
self . resize ( self . sizeHint ( ) )
def kindle_serials ( self ) :
2020-09-27 12:54:49 +02:00
d = ManageKeysDialog ( self , " EInk Kindle Serial Number " , self . tempdedrmprefs [ ' serials ' ] , AddSerialDialog )
2013-03-20 11:23:54 +01:00
d . exec_ ( )
2020-09-26 22:22:47 +02:00
2015-07-29 19:11:19 +02:00
def kindle_android ( self ) :
2020-09-27 12:54:49 +02:00
d = ManageKeysDialog ( self , " Kindle for Android Key " , self . tempdedrmprefs [ ' androidkeys ' ] , AddAndroidDialog , ' k4a ' )
2015-03-17 18:49:30 +01:00
d . exec_ ( )
2013-03-20 11:23:54 +01:00
def kindle_keys ( self ) :
2013-04-05 18:44:48 +02:00
if isosx or iswindows :
2020-09-27 12:54:49 +02:00
d = ManageKeysDialog ( self , " Kindle for Mac and PC Key " , self . tempdedrmprefs [ ' kindlekeys ' ] , AddKindleDialog , ' k4i ' )
2013-04-05 18:44:48 +02:00
else :
# linux
2020-09-27 12:54:49 +02:00
d = ManageKeysDialog ( self , " Kindle for Mac and PC Key " , self . tempdedrmprefs [ ' kindlekeys ' ] , AddKindleDialog , ' k4i ' , self . tempdedrmprefs [ ' kindlewineprefix ' ] )
2013-03-20 11:23:54 +01:00
d . exec_ ( )
2013-04-05 18:44:48 +02:00
self . tempdedrmprefs [ ' kindlewineprefix ' ] = d . getwineprefix ( )
2013-03-20 11:23:54 +01:00
def adept_keys ( self ) :
2013-04-05 18:44:48 +02:00
if isosx or iswindows :
2020-09-27 12:54:49 +02:00
d = ManageKeysDialog ( self , " Adobe Digital Editions Key " , self . tempdedrmprefs [ ' adeptkeys ' ] , AddAdeptDialog , ' der ' )
2013-04-05 18:44:48 +02:00
else :
# linux
2020-09-27 12:54:49 +02:00
d = ManageKeysDialog ( self , " Adobe Digital Editions Key " , self . tempdedrmprefs [ ' adeptkeys ' ] , AddAdeptDialog , ' der ' , self . tempdedrmprefs [ ' adobewineprefix ' ] )
2013-03-20 11:23:54 +01:00
d . exec_ ( )
2013-04-05 18:44:48 +02:00
self . tempdedrmprefs [ ' adobewineprefix ' ] = d . getwineprefix ( )
2013-03-20 11:23:54 +01:00
def mobi_keys ( self ) :
2020-09-27 12:54:49 +02:00
d = ManageKeysDialog ( self , " Mobipocket PID " , self . tempdedrmprefs [ ' pids ' ] , AddPIDDialog )
2013-03-20 11:23:54 +01:00
d . exec_ ( )
def bandn_keys ( self ) :
2021-12-23 11:29:58 +01:00
d = ManageKeysDialog ( self , " ADE PassHash Key " , self . tempdedrmprefs [ ' bandnkeys ' ] , AddBandNKeyDialog , ' b64 ' )
2013-03-20 11:23:54 +01:00
d . exec_ ( )
def ereader_keys ( self ) :
2020-09-27 12:54:49 +02:00
d = ManageKeysDialog ( self , " eReader Key " , self . tempdedrmprefs [ ' ereaderkeys ' ] , AddEReaderDialog , ' b63 ' )
2013-03-20 11:23:54 +01:00
d . exec_ ( )
2021-11-17 21:53:24 +01:00
def readium_lcp_keys ( self ) :
d = ManageKeysDialog ( self , " Readium LCP passphrase " , self . tempdedrmprefs [ ' lcp_passphrases ' ] , AddLCPKeyDialog )
d . exec_ ( )
2013-03-20 11:23:54 +01:00
def help_link_activated ( self , url ) :
def get_help_file_resource ( ) :
# Copy the HTML helpfile to the plugin directory each time the
# link is clicked in case the helpfile is updated in newer plugins.
2020-09-27 12:54:49 +02:00
file_path = os . path . join ( config_dir , " plugins " , " DeDRM " , " help " , help_file_name )
2020-10-14 17:23:49 +02:00
with open ( file_path , ' w ' ) as f :
2013-03-20 11:23:54 +01:00
f . write ( self . load_resource ( help_file_name ) )
return file_path
url = ' file:/// ' + get_help_file_resource ( )
open_url ( QUrl ( url ) )
def save_settings ( self ) :
2013-04-05 18:44:48 +02:00
self . dedrmprefs . set ( ' bandnkeys ' , self . tempdedrmprefs [ ' bandnkeys ' ] )
self . dedrmprefs . set ( ' adeptkeys ' , self . tempdedrmprefs [ ' adeptkeys ' ] )
self . dedrmprefs . set ( ' ereaderkeys ' , self . tempdedrmprefs [ ' ereaderkeys ' ] )
self . dedrmprefs . set ( ' kindlekeys ' , self . tempdedrmprefs [ ' kindlekeys ' ] )
2015-07-29 19:11:19 +02:00
self . dedrmprefs . set ( ' androidkeys ' , self . tempdedrmprefs [ ' androidkeys ' ] )
2013-04-05 18:44:48 +02:00
self . dedrmprefs . set ( ' pids ' , self . tempdedrmprefs [ ' pids ' ] )
self . dedrmprefs . set ( ' serials ' , self . tempdedrmprefs [ ' serials ' ] )
self . dedrmprefs . set ( ' adobewineprefix ' , self . tempdedrmprefs [ ' adobewineprefix ' ] )
self . dedrmprefs . set ( ' kindlewineprefix ' , self . tempdedrmprefs [ ' kindlewineprefix ' ] )
self . dedrmprefs . set ( ' configured ' , True )
2021-11-15 17:59:48 +01:00
self . dedrmprefs . set ( ' deobfuscate_fonts ' , self . chkFontObfuscation . isChecked ( ) )
2021-11-17 16:17:30 +01:00
self . dedrmprefs . set ( ' remove_watermarks ' , self . chkRemoveWatermarks . isChecked ( ) )
2021-11-17 21:53:24 +01:00
self . dedrmprefs . set ( ' lcp_passphrases ' , self . tempdedrmprefs [ ' lcp_passphrases ' ] )
2013-04-05 18:44:48 +02:00
self . dedrmprefs . writeprefs ( )
2013-03-20 11:23:54 +01:00
def load_resource ( self , name ) :
with ZipFile ( self . plugin_path , ' r ' ) as zf :
if name in zf . namelist ( ) :
2020-12-03 12:02:09 +01:00
return zf . read ( name ) . decode ( ' utf-8 ' )
2013-03-20 11:23:54 +01:00
return " "
2013-04-05 18:44:48 +02:00
class ManageKeysDialog ( QDialog ) :
2020-10-14 17:23:49 +02:00
def __init__ ( self , parent , key_type_name , plugin_keys , create_key , keyfile_ext = " " , wineprefix = None ) :
2013-04-05 18:44:48 +02:00
QDialog . __init__ ( self , parent )
self . parent = parent
self . key_type_name = key_type_name
self . plugin_keys = plugin_keys
self . create_key = create_key
self . keyfile_ext = keyfile_ext
2020-10-14 17:23:49 +02:00
self . import_key = ( keyfile_ext != " " )
2020-09-27 12:54:49 +02:00
self . binary_file = ( keyfile_ext == " der " )
self . json_file = ( keyfile_ext == " k4i " )
self . android_file = ( keyfile_ext == " k4a " )
2013-04-05 18:44:48 +02:00
self . wineprefix = wineprefix
self . setWindowTitle ( " {0} {1} : Manage {2} s " . format ( PLUGIN_NAME , PLUGIN_VERSION , self . key_type_name ) )
# Start Qt Gui dialog layout
layout = QVBoxLayout ( self )
self . setLayout ( layout )
help_layout = QHBoxLayout ( )
layout . addLayout ( help_layout )
# Add hyperlink to a help file at the right. We will replace the correct name when it is clicked.
help_label = QLabel ( ' <a href= " http://www.foo.com/ " >Help</a> ' , self )
help_label . setTextInteractionFlags ( Qt . LinksAccessibleByMouse | Qt . LinksAccessibleByKeyboard )
help_label . setAlignment ( Qt . AlignRight )
help_label . linkActivated . connect ( self . help_link_activated )
help_layout . addWidget ( help_label )
2020-09-27 12:54:49 +02:00
keys_group_box = QGroupBox ( _ ( " {0} s " . format ( self . key_type_name ) ) , self )
2013-04-05 18:44:48 +02:00
layout . addWidget ( keys_group_box )
keys_group_box_layout = QHBoxLayout ( )
keys_group_box . setLayout ( keys_group_box_layout )
self . listy = QListWidget ( self )
2020-09-27 12:54:49 +02:00
self . listy . setToolTip ( " {0} s that will be used to decrypt ebooks " . format ( self . key_type_name ) )
2013-04-05 18:44:48 +02:00
self . listy . setSelectionMode ( QAbstractItemView . SingleSelection )
self . populate_list ( )
keys_group_box_layout . addWidget ( self . listy )
button_layout = QVBoxLayout ( )
keys_group_box_layout . addLayout ( button_layout )
self . _add_key_button = QtGui . QToolButton ( self )
self . _add_key_button . setIcon ( QIcon ( I ( ' plus.png ' ) ) )
2020-09-27 12:54:49 +02:00
self . _add_key_button . setToolTip ( " Create new {0} " . format ( self . key_type_name ) )
2013-04-05 18:44:48 +02:00
self . _add_key_button . clicked . connect ( self . add_key )
button_layout . addWidget ( self . _add_key_button )
self . _delete_key_button = QtGui . QToolButton ( self )
2020-09-27 12:54:49 +02:00
self . _delete_key_button . setToolTip ( _ ( " Delete highlighted key " ) )
2013-04-05 18:44:48 +02:00
self . _delete_key_button . setIcon ( QIcon ( I ( ' list_remove.png ' ) ) )
self . _delete_key_button . clicked . connect ( self . delete_key )
button_layout . addWidget ( self . _delete_key_button )
if type ( self . plugin_keys ) == dict and self . import_key :
self . _rename_key_button = QtGui . QToolButton ( self )
2020-09-27 12:54:49 +02:00
self . _rename_key_button . setToolTip ( _ ( " Rename highlighted key " ) )
2013-04-05 18:44:48 +02:00
self . _rename_key_button . setIcon ( QIcon ( I ( ' edit-select-all.png ' ) ) )
self . _rename_key_button . clicked . connect ( self . rename_key )
button_layout . addWidget ( self . _rename_key_button )
self . export_key_button = QtGui . QToolButton ( self )
2020-09-27 12:54:49 +02:00
self . export_key_button . setToolTip ( " Save highlighted key to a . {0} file " . format ( self . keyfile_ext ) )
2013-04-05 18:44:48 +02:00
self . export_key_button . setIcon ( QIcon ( I ( ' save.png ' ) ) )
self . export_key_button . clicked . connect ( self . export_key )
button_layout . addWidget ( self . export_key_button )
spacerItem = QtGui . QSpacerItem ( 20 , 40 , QtGui . QSizePolicy . Minimum , QtGui . QSizePolicy . Expanding )
button_layout . addItem ( spacerItem )
if self . wineprefix is not None :
layout . addSpacing ( 5 )
wineprefix_layout = QHBoxLayout ( )
layout . addLayout ( wineprefix_layout )
wineprefix_layout . setAlignment ( Qt . AlignCenter )
2020-09-27 12:54:49 +02:00
self . wp_label = QLabel ( " WINEPREFIX: " )
2013-04-05 18:44:48 +02:00
wineprefix_layout . addWidget ( self . wp_label )
self . wp_lineedit = QLineEdit ( self )
wineprefix_layout . addWidget ( self . wp_lineedit )
self . wp_label . setBuddy ( self . wp_lineedit )
self . wp_lineedit . setText ( self . wineprefix )
layout . addSpacing ( 5 )
migrate_layout = QHBoxLayout ( )
layout . addLayout ( migrate_layout )
if self . import_key :
migrate_layout . setAlignment ( Qt . AlignJustify )
2020-09-27 12:54:49 +02:00
self . migrate_btn = QPushButton ( " Import Existing Keyfiles " , self )
self . migrate_btn . setToolTip ( " Import *. {0} files (created using other tools). " . format ( self . keyfile_ext ) )
2013-04-05 18:44:48 +02:00
self . migrate_btn . clicked . connect ( self . migrate_wrapper )
migrate_layout . addWidget ( self . migrate_btn )
migrate_layout . addStretch ( )
self . button_box = QDialogButtonBox ( QDialogButtonBox . Close )
self . button_box . rejected . connect ( self . close )
migrate_layout . addWidget ( self . button_box )
self . resize ( self . sizeHint ( ) )
def getwineprefix ( self ) :
if self . wineprefix is not None :
2020-10-14 17:23:49 +02:00
return str ( self . wp_lineedit . text ( ) ) . strip ( )
return " "
2013-04-05 18:44:48 +02:00
def populate_list ( self ) :
if type ( self . plugin_keys ) == dict :
for key in self . plugin_keys . keys ( ) :
self . listy . addItem ( QListWidgetItem ( key ) )
else :
for key in self . plugin_keys :
self . listy . addItem ( QListWidgetItem ( key ) )
2021-11-15 10:56:26 +01:00
self . listy . setMinimumWidth ( self . listy . sizeHintForColumn ( 0 ) + 20 )
2013-04-05 18:44:48 +02:00
def add_key ( self ) :
d = self . create_key ( self )
d . exec_ ( )
if d . result ( ) != d . Accepted :
# New key generation cancelled.
return
2021-12-24 14:35:53 +01:00
if d . k_key_list is not None :
# importing multiple keys
idx = - 1
dup_key_count = 0
added_key_count = 0
while True :
idx = idx + 1
try :
new_key_value = d . k_key_list [ idx ]
except :
break
if type ( self . plugin_keys ) == dict :
if new_key_value in self . plugin_keys . values ( ) :
dup_key_count = dup_key_count + 1
continue
self . plugin_keys [ d . k_name_list [ idx ] ] = new_key_value
added_key_count = added_key_count + 1
else :
if new_key_value in self . plugin_keys :
dup_key_count = dup_key_count + 1
continue
self . plugin_keys . append ( new_key_value )
added_key_count = added_key_count + 1
2021-12-25 23:35:59 +01:00
if ( added_key_count > 0 or dup_key_count > 0 ) :
if ( added_key_count == 0 ) :
info_dialog ( None , " {0} {1} : Adding {2} " . format ( PLUGIN_NAME , PLUGIN_VERSION , self . key_type_name ) ,
" Skipped adding {0} duplicate / existing keys. " . format ( dup_key_count ) , show = True , show_copy_button = False )
elif ( dup_key_count == 0 ) :
info_dialog ( None , " {0} {1} : Adding {2} " . format ( PLUGIN_NAME , PLUGIN_VERSION , self . key_type_name ) ,
" Added {0} new keys. " . format ( added_key_count ) , show = True , show_copy_button = False )
else :
info_dialog ( None , " {0} {1} : Adding {2} " . format ( PLUGIN_NAME , PLUGIN_VERSION , self . key_type_name ) ,
" Added {0} new keys, skipped adding {1} existing keys. " . format ( added_key_count , dup_key_count ) , show = True , show_copy_button = False )
2021-12-24 14:35:53 +01:00
2013-04-05 18:44:48 +02:00
else :
2021-12-24 14:35:53 +01:00
# Import single key
new_key_value = d . key_value
if type ( self . plugin_keys ) == dict :
if new_key_value in self . plugin_keys . values ( ) :
old_key_name = [ name for name , value in self . plugin_keys . items ( ) if value == new_key_value ] [ 0 ]
info_dialog ( None , " {0} {1} : Duplicate {2} " . format ( PLUGIN_NAME , PLUGIN_VERSION , self . key_type_name ) ,
" The new {1} is the same as the existing {1} named <strong> {0} </strong> and has not been added. " . format ( old_key_name , self . key_type_name ) , show = True )
return
self . plugin_keys [ d . key_name ] = new_key_value
else :
if new_key_value in self . plugin_keys :
info_dialog ( None , " {0} {1} : Duplicate {2} " . format ( PLUGIN_NAME , PLUGIN_VERSION , self . key_type_name ) ,
" This {0} is already in the list of {0} s has not been added. " . format ( self . key_type_name ) , show = True )
return
self . plugin_keys . append ( d . key_value )
2013-04-05 18:44:48 +02:00
self . listy . clear ( )
self . populate_list ( )
def rename_key ( self ) :
if not self . listy . currentItem ( ) :
2020-09-27 12:54:49 +02:00
errmsg = " No {0} selected to rename. Highlight a keyfile first. " . format ( self . key_type_name )
2013-04-05 18:44:48 +02:00
r = error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) ,
_ ( errmsg ) , show = True , show_copy_button = False )
return
d = RenameKeyDialog ( self )
d . exec_ ( )
if d . result ( ) != d . Accepted :
# rename cancelled or moot.
return
2020-10-14 17:23:49 +02:00
keyname = str ( self . listy . currentItem ( ) . text ( ) )
2020-09-27 12:54:49 +02:00
if not question_dialog ( self , " {0} {1} : Confirm Rename " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , " Do you really want to rename the {2} named <strong> {0} </strong> to <strong> {1} </strong>? " . format ( keyname , d . key_name , self . key_type_name ) , show_copy_button = False , default_yes = False ) :
2013-04-05 18:44:48 +02:00
return
self . plugin_keys [ d . key_name ] = self . plugin_keys [ keyname ]
del self . plugin_keys [ keyname ]
self . listy . clear ( )
self . populate_list ( )
def delete_key ( self ) :
if not self . listy . currentItem ( ) :
return
2020-10-14 17:23:49 +02:00
keyname = str ( self . listy . currentItem ( ) . text ( ) )
2020-09-27 12:54:49 +02:00
if not question_dialog ( self , " {0} {1} : Confirm Delete " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , " Do you really want to delete the {1} <strong> {0} </strong>? " . format ( keyname , self . key_type_name ) , show_copy_button = False , default_yes = False ) :
2013-04-05 18:44:48 +02:00
return
if type ( self . plugin_keys ) == dict :
del self . plugin_keys [ keyname ]
else :
self . plugin_keys . remove ( keyname )
self . listy . clear ( )
self . populate_list ( )
def help_link_activated ( self , url ) :
def get_help_file_resource ( ) :
# Copy the HTML helpfile to the plugin directory each time the
# link is clicked in case the helpfile is updated in newer plugins.
2020-09-27 12:54:49 +02:00
help_file_name = " {0} _ {1} _Help.htm " . format ( PLUGIN_NAME , self . key_type_name )
file_path = os . path . join ( config_dir , " plugins " , " DeDRM " , " help " , help_file_name )
2020-10-14 17:23:49 +02:00
with open ( file_path , ' w ' ) as f :
2013-04-05 18:44:48 +02:00
f . write ( self . parent . load_resource ( help_file_name ) )
return file_path
url = ' file:/// ' + get_help_file_resource ( )
open_url ( QUrl ( url ) )
def migrate_files ( self ) :
2020-09-27 12:54:49 +02:00
unique_dlg_name = PLUGIN_NAME + " import {0} keys " . format ( self . key_type_name ) . replace ( ' ' , ' _ ' ) #takes care of automatically remembering last directory
caption = " Select {0} files to import " . format ( self . key_type_name )
filters = [ ( " {0} files " . format ( self . key_type_name ) , [ self . keyfile_ext ] ) ]
2015-03-07 22:18:50 +01:00
files = choose_files ( self , unique_dlg_name , caption , filters , all_files = False )
2013-04-05 18:44:48 +02:00
counter = 0
skipped = 0
if files :
2013-03-20 11:23:54 +01:00
for filename in files :
fpath = os . path . join ( config_dir , filename )
2013-04-05 18:44:48 +02:00
filename = os . path . basename ( filename )
2015-07-29 19:11:19 +02:00
new_key_name = os . path . splitext ( os . path . basename ( filename ) ) [ 0 ]
with open ( fpath , ' rb ' ) as keyfile :
new_key_value = keyfile . read ( )
if self . binary_file :
2020-11-22 16:03:45 +01:00
new_key_value = codecs . encode ( new_key_value , ' hex ' )
2015-07-29 19:11:19 +02:00
elif self . json_file :
new_key_value = json . loads ( new_key_value )
elif self . android_file :
# convert to list of the keys in the string
new_key_value = new_key_value . splitlines ( )
match = False
for key in self . plugin_keys . keys ( ) :
if uStrCmp ( new_key_name , key , True ) :
skipped + = 1
2020-09-27 12:54:49 +02:00
msg = " A key with the name <strong> {0} </strong> already exists! \n Skipping key file <strong> {1} </strong>. \n Rename the existing key and import again " . format ( new_key_name , filename )
2015-07-29 19:11:19 +02:00
inf = info_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) ,
_ ( msg ) , show_copy_button = False , show = True )
match = True
break
if not match :
if new_key_value in self . plugin_keys . values ( ) :
2020-10-14 17:23:49 +02:00
old_key_name = [ name for name , value in self . plugin_keys . items ( ) if value == new_key_value ] [ 0 ]
2015-07-29 19:11:19 +02:00
skipped + = 1
info_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) ,
2020-09-27 12:54:49 +02:00
" The key in file {0} is the same as the existing key <strong> {1} </strong> and has been skipped. " . format ( filename , old_key_name ) , show_copy_button = False , show = True )
2015-07-29 19:11:19 +02:00
else :
counter + = 1
self . plugin_keys [ new_key_name ] = new_key_value
2020-09-26 22:22:47 +02:00
2020-10-14 17:23:49 +02:00
msg = " "
2013-04-05 18:44:48 +02:00
if counter + skipped > 1 :
if counter > 0 :
2020-09-27 12:54:49 +02:00
msg + = " Imported <strong> {0:d} </strong> key {1} . " . format ( counter , " file " if counter == 1 else " files " )
2013-04-05 18:44:48 +02:00
if skipped > 0 :
2020-09-27 12:54:49 +02:00
msg + = " Skipped <strong> {0:d} </strong> key {1} . " . format ( skipped , " file " if counter == 1 else " files " )
2013-04-05 18:44:48 +02:00
inf = info_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) ,
_ ( msg ) , show_copy_button = False , show = True )
return counter > 0
def migrate_wrapper ( self ) :
if self . migrate_files ( ) :
self . listy . clear ( )
self . populate_list ( )
def export_key ( self ) :
if not self . listy . currentItem ( ) :
2020-09-27 12:54:49 +02:00
errmsg = " No keyfile selected to export. Highlight a keyfile first. "
2013-04-05 18:44:48 +02:00
r = error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) ,
_ ( errmsg ) , show = True , show_copy_button = False )
return
2020-10-14 17:23:49 +02:00
keyname = str ( self . listy . currentItem ( ) . text ( ) )
2020-09-27 12:54:49 +02:00
unique_dlg_name = PLUGIN_NAME + " export {0} keys " . format ( self . key_type_name ) . replace ( ' ' , ' _ ' ) #takes care of automatically remembering last directory
caption = " Save {0} File as... " . format ( self . key_type_name )
filters = [ ( " {0} Files " . format ( self . key_type_name ) , [ " {0} " . format ( self . keyfile_ext ) ] ) ]
defaultname = " {0} . {1} " . format ( keyname , self . keyfile_ext )
2015-03-07 22:18:50 +01:00
filename = choose_save_file ( self , unique_dlg_name , caption , filters , all_files = False , initial_filename = defaultname )
2013-04-05 18:44:48 +02:00
if filename :
2020-11-06 23:49:18 +01:00
if self . binary_file :
with open ( filename , ' wb ' ) as fname :
2020-11-22 16:03:45 +01:00
fname . write ( codecs . decode ( self . plugin_keys [ keyname ] , ' hex ' ) )
2020-11-06 23:49:18 +01:00
elif self . json_file :
with open ( filename , ' w ' ) as fname :
2020-10-14 17:23:49 +02:00
fname . write ( json . dumps ( self . plugin_keys [ keyname ] ) )
2020-11-06 23:49:18 +01:00
elif self . android_file :
with open ( filename , ' w ' ) as fname :
2015-07-29 19:11:19 +02:00
for key in self . plugin_keys [ keyname ] :
2020-11-22 16:03:45 +01:00
fname . write ( key )
2020-11-06 23:49:18 +01:00
fname . write ( ' \n ' )
else :
with open ( filename , ' w ' ) as fname :
2020-11-22 16:03:45 +01:00
fname . write ( self . plugin_keys [ keyname ] )
2013-04-05 18:44:48 +02:00
class RenameKeyDialog ( QDialog ) :
def __init__ ( self , parent = None , ) :
QDialog . __init__ ( self , parent )
self . parent = parent
self . setWindowTitle ( " {0} {1} : Rename {0} " . format ( PLUGIN_NAME , PLUGIN_VERSION , parent . key_type_name ) )
layout = QVBoxLayout ( self )
self . setLayout ( layout )
data_group_box = QGroupBox ( ' ' , self )
layout . addWidget ( data_group_box )
data_group_box_layout = QVBoxLayout ( )
data_group_box . setLayout ( data_group_box_layout )
data_group_box_layout . addWidget ( QLabel ( ' New Key Name: ' , self ) )
self . key_ledit = QLineEdit ( self . parent . listy . currentItem ( ) . text ( ) , self )
2020-09-27 12:54:49 +02:00
self . key_ledit . setToolTip ( " Enter a new name for this existing {0} . " . format ( parent . key_type_name ) )
2013-04-05 18:44:48 +02:00
data_group_box_layout . addWidget ( self . key_ledit )
layout . addSpacing ( 20 )
self . button_box = QDialogButtonBox ( QDialogButtonBox . Ok | QDialogButtonBox . Cancel )
self . button_box . accepted . connect ( self . accept )
self . button_box . rejected . connect ( self . reject )
layout . addWidget ( self . button_box )
self . resize ( self . sizeHint ( ) )
def accept ( self ) :
2020-10-14 17:23:49 +02:00
if not str ( self . key_ledit . text ( ) ) or str ( self . key_ledit . text ( ) ) . isspace ( ) :
2020-09-27 12:54:49 +02:00
errmsg = " Key name field cannot be empty! "
2013-04-05 18:44:48 +02:00
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) ,
_ ( errmsg ) , show = True , show_copy_button = False )
if len ( self . key_ledit . text ( ) ) < 4 :
2020-09-27 12:54:49 +02:00
errmsg = " Key name must be at <i>least</i> 4 characters long! "
2013-04-05 18:44:48 +02:00
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) ,
_ ( errmsg ) , show = True , show_copy_button = False )
if uStrCmp ( self . key_ledit . text ( ) , self . parent . listy . currentItem ( ) . text ( ) ) :
# Same exact name ... do nothing.
return QDialog . reject ( self )
for k in self . parent . plugin_keys . keys ( ) :
if ( uStrCmp ( self . key_ledit . text ( ) , k , True ) and
not uStrCmp ( k , self . parent . listy . currentItem ( ) . text ( ) , True ) ) :
2020-09-27 12:54:49 +02:00
errmsg = " The key name <strong> {0} </strong> is already being used. " . format ( self . key_ledit . text ( ) )
2013-04-05 18:44:48 +02:00
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) ,
_ ( errmsg ) , show = True , show_copy_button = False )
QDialog . accept ( self )
@property
def key_name ( self ) :
2020-10-14 17:23:49 +02:00
return str ( self . key_ledit . text ( ) ) . strip ( )
2013-04-05 18:44:48 +02:00
class AddBandNKeyDialog ( QDialog ) :
2021-12-23 11:29:58 +01:00
def update_form ( self , idx ) :
self . cbType . hide ( )
2013-04-05 18:44:48 +02:00
2021-12-23 11:29:58 +01:00
if idx == 1 :
self . add_fields_for_passhash ( )
elif idx == 2 :
self . add_fields_for_b64_passhash ( )
2021-12-24 14:35:53 +01:00
elif idx == 3 :
self . add_fields_for_ade_passhash ( )
2021-12-23 11:29:58 +01:00
elif idx == 4 :
2021-12-24 14:35:53 +01:00
self . add_fields_for_windows_nook ( )
elif idx == 5 :
2021-12-23 11:29:58 +01:00
self . add_fields_for_android_nook ( )
2021-12-24 14:35:53 +01:00
def add_fields_for_ade_passhash ( self ) :
self . ade_extr_group_box = QGroupBox ( " " , self )
ade_extr_group_box_layout = QVBoxLayout ( )
self . ade_extr_group_box . setLayout ( ade_extr_group_box_layout )
self . layout . addWidget ( self . ade_extr_group_box )
ade_extr_group_box_layout . addWidget ( QLabel ( " Click \" OK \" to try and dump PassHash data \n from Adobe Digital Editions. This works if \n you ' ve opened your PassHash books in ADE before. " , self ) )
self . button_box . hide ( )
self . button_box = QDialogButtonBox ( QDialogButtonBox . Ok | QDialogButtonBox . Cancel )
self . button_box . accepted . connect ( self . accept_ade_dump_passhash )
self . button_box . rejected . connect ( self . reject )
self . layout . addWidget ( self . button_box )
self . resize ( self . sizeHint ( ) )
2021-12-23 11:29:58 +01:00
def add_fields_for_android_nook ( self ) :
self . andr_nook_group_box = QGroupBox ( " " , self )
andr_nook_group_box_layout = QVBoxLayout ( )
self . andr_nook_group_box . setLayout ( andr_nook_group_box_layout )
self . layout . addWidget ( self . andr_nook_group_box )
ph_key_name_group = QHBoxLayout ( )
andr_nook_group_box_layout . addLayout ( ph_key_name_group )
ph_key_name_group . addWidget ( QLabel ( " Unique Key Name: " , self ) )
self . key_ledit = QLineEdit ( " " , self )
self . key_ledit . setToolTip ( _ ( " <p>Enter an identifying name for this new key.</p> " ) )
ph_key_name_group . addWidget ( self . key_ledit )
andr_nook_group_box_layout . addWidget ( QLabel ( " Hidden in the Android application data is a " +
" folder \n named ' .adobe-digital-editions ' . Please enter \n the full path to that folder. " , self ) )
ph_path_group = QHBoxLayout ( )
andr_nook_group_box_layout . addLayout ( ph_path_group )
ph_path_group . addWidget ( QLabel ( " Path: " , self ) )
self . cc_ledit = QLineEdit ( " " , self )
self . cc_ledit . setToolTip ( _ ( " <p>Enter path to .adobe-digital-editions folder.</p> " ) )
ph_path_group . addWidget ( self . cc_ledit )
self . button_box . hide ( )
self . button_box = QDialogButtonBox ( QDialogButtonBox . Ok | QDialogButtonBox . Cancel )
self . button_box . accepted . connect ( self . accept_android_nook )
self . button_box . rejected . connect ( self . reject )
self . layout . addWidget ( self . button_box )
self . resize ( self . sizeHint ( ) )
def add_fields_for_windows_nook ( self ) :
self . win_nook_group_box = QGroupBox ( " " , self )
win_nook_group_box_layout = QVBoxLayout ( )
self . win_nook_group_box . setLayout ( win_nook_group_box_layout )
self . layout . addWidget ( self . win_nook_group_box )
ph_key_name_group = QHBoxLayout ( )
win_nook_group_box_layout . addLayout ( ph_key_name_group )
ph_key_name_group . addWidget ( QLabel ( " Unique Key Name: " , self ) )
self . key_ledit = QLineEdit ( " " , self )
self . key_ledit . setToolTip ( _ ( " <p>Enter an identifying name for this new key.</p> " ) )
ph_key_name_group . addWidget ( self . key_ledit )
self . button_box . hide ( )
self . button_box = QDialogButtonBox ( QDialogButtonBox . Ok | QDialogButtonBox . Cancel )
self . button_box . accepted . connect ( self . accept_win_nook )
self . button_box . rejected . connect ( self . reject )
self . layout . addWidget ( self . button_box )
self . resize ( self . sizeHint ( ) )
def add_fields_for_b64_passhash ( self ) :
self . passhash_group_box = QGroupBox ( " " , self )
passhash_group_box_layout = QVBoxLayout ( )
self . passhash_group_box . setLayout ( passhash_group_box_layout )
self . layout . addWidget ( self . passhash_group_box )
ph_key_name_group = QHBoxLayout ( )
passhash_group_box_layout . addLayout ( ph_key_name_group )
ph_key_name_group . addWidget ( QLabel ( " Unique Key Name: " , self ) )
2013-04-05 18:44:48 +02:00
self . key_ledit = QLineEdit ( " " , self )
2020-09-27 12:54:49 +02:00
self . key_ledit . setToolTip ( _ ( " <p>Enter an identifying name for this new key.</p> " +
" <p>It should be something that will help you remember " +
" what personal information was used to create it. " ) )
2021-12-23 11:29:58 +01:00
ph_key_name_group . addWidget ( self . key_ledit )
2013-04-05 18:44:48 +02:00
2021-12-23 11:29:58 +01:00
ph_name_group = QHBoxLayout ( )
passhash_group_box_layout . addLayout ( ph_name_group )
ph_name_group . addWidget ( QLabel ( " Base64 key string: " , self ) )
2020-10-14 17:23:49 +02:00
self . cc_ledit = QLineEdit ( " " , self )
2021-12-23 11:29:58 +01:00
self . cc_ledit . setToolTip ( _ ( " <p>Enter the Base64 key string</p> " ) )
ph_name_group . addWidget ( self . cc_ledit )
self . button_box . hide ( )
self . button_box = QDialogButtonBox ( QDialogButtonBox . Ok | QDialogButtonBox . Cancel )
self . button_box . accepted . connect ( self . accept_b64_passhash )
self . button_box . rejected . connect ( self . reject )
self . layout . addWidget ( self . button_box )
self . resize ( self . sizeHint ( ) )
2021-11-15 14:30:32 +01:00
2016-04-25 18:49:06 +02:00
2021-12-23 11:29:58 +01:00
def add_fields_for_passhash ( self ) :
self . passhash_group_box = QGroupBox ( " " , self )
passhash_group_box_layout = QVBoxLayout ( )
self . passhash_group_box . setLayout ( passhash_group_box_layout )
self . layout . addWidget ( self . passhash_group_box )
ph_key_name_group = QHBoxLayout ( )
passhash_group_box_layout . addLayout ( ph_key_name_group )
ph_key_name_group . addWidget ( QLabel ( " Unique Key Name: " , self ) )
self . key_ledit = QLineEdit ( " " , self )
self . key_ledit . setToolTip ( _ ( " <p>Enter an identifying name for this new key.</p> " +
" <p>It should be something that will help you remember " +
" what personal information was used to create it. " ) )
ph_key_name_group . addWidget ( self . key_ledit )
ph_name_group = QHBoxLayout ( )
passhash_group_box_layout . addLayout ( ph_name_group )
ph_name_group . addWidget ( QLabel ( " Username: " , self ) )
self . name_ledit = QLineEdit ( " " , self )
self . name_ledit . setToolTip ( _ ( " <p>Enter the PassHash username</p> " ) )
ph_name_group . addWidget ( self . name_ledit )
ph_pass_group = QHBoxLayout ( )
passhash_group_box_layout . addLayout ( ph_pass_group )
ph_pass_group . addWidget ( QLabel ( " Password: " , self ) )
self . cc_ledit = QLineEdit ( " " , self )
self . cc_ledit . setToolTip ( _ ( " <p>Enter the PassHash password</p> " ) )
ph_pass_group . addWidget ( self . cc_ledit )
self . button_box . hide ( )
2013-04-05 18:44:48 +02:00
self . button_box = QDialogButtonBox ( QDialogButtonBox . Ok | QDialogButtonBox . Cancel )
2021-12-23 11:29:58 +01:00
self . button_box . accepted . connect ( self . accept_passhash )
2013-04-05 18:44:48 +02:00
self . button_box . rejected . connect ( self . reject )
2021-12-23 11:29:58 +01:00
self . layout . addWidget ( self . button_box )
self . resize ( self . sizeHint ( ) )
def __init__ ( self , parent = None , ) :
QDialog . __init__ ( self , parent )
self . parent = parent
self . setWindowTitle ( " {0} {1} : Create New PassHash (B&N) Key " . format ( PLUGIN_NAME , PLUGIN_VERSION ) )
self . layout = QVBoxLayout ( self )
self . setLayout ( self . layout )
self . cbType = QComboBox ( )
self . cbType . addItem ( " --- Select key type --- " )
self . cbType . addItem ( " Adobe PassHash username & password " )
self . cbType . addItem ( " Base64-encoded PassHash key string " )
2021-12-24 14:35:53 +01:00
self . cbType . addItem ( " Extract passhashes from Adobe Digital Editions " )
2021-12-23 11:29:58 +01:00
self . cbType . addItem ( " Extract key from Nook Windows application " )
self . cbType . addItem ( " Extract key from Nook Android application " )
self . cbType . currentIndexChanged . connect ( self . update_form , self . cbType . currentIndex ( ) )
self . layout . addWidget ( self . cbType )
self . button_box = QDialogButtonBox ( QDialogButtonBox . Cancel )
self . button_box . rejected . connect ( self . reject )
self . layout . addWidget ( self . button_box )
2013-04-05 18:44:48 +02:00
self . resize ( self . sizeHint ( ) )
@property
def key_name ( self ) :
2021-12-24 14:35:53 +01:00
try :
return str ( self . key_ledit . text ( ) ) . strip ( )
except :
return self . result_data_name
2013-04-05 18:44:48 +02:00
@property
def key_value ( self ) :
2021-12-23 11:29:58 +01:00
return self . result_data
2013-04-05 18:44:48 +02:00
@property
def user_name ( self ) :
2020-10-14 17:23:49 +02:00
return str ( self . name_ledit . text ( ) ) . strip ( ) . lower ( ) . replace ( ' ' , ' ' )
2013-04-05 18:44:48 +02:00
@property
def cc_number ( self ) :
2020-10-14 17:23:49 +02:00
return str ( self . cc_ledit . text ( ) ) . strip ( )
2013-04-05 18:44:48 +02:00
2021-12-24 14:35:53 +01:00
@property
def k_name_list ( self ) :
# If the plugin supports returning multiple keys, return a list of names.
if self . k_full_name_list is not None and self . k_full_key_list is not None :
return self . k_full_name_list
return None
@property
def k_key_list ( self ) :
# If the plugin supports returning multiple keys, return a list of keys.
if self . k_full_name_list is not None and self . k_full_key_list is not None :
return self . k_full_key_list
return None
2021-12-23 11:29:58 +01:00
def accept_android_nook ( self ) :
if len ( self . key_name ) < 4 :
errmsg = " Key name must be at <i>least</i> 4 characters long! "
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
path_to_ade_data = self . cc_number
if ( os . path . isfile ( os . path . join ( path_to_ade_data , " .adobe-digital-editions " , " activation.xml " ) ) ) :
path_to_ade_data = os . path . join ( path_to_ade_data , " .adobe-digital-editions " )
elif ( os . path . isfile ( os . path . join ( path_to_ade_data , " activation.xml " ) ) ) :
pass
2021-11-15 14:30:32 +01:00
else :
2021-12-23 11:29:58 +01:00
errmsg = " This isn ' t the correct path, or the data is invalid. "
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
2013-04-05 18:44:48 +02:00
2021-12-23 11:29:58 +01:00
from calibre_plugins . dedrm . ignoblekeyAndroid import dump_keys
store_result = dump_keys ( path_to_ade_data )
if len ( store_result ) == 0 :
errmsg = " Failed to extract keys. Is this the correct folder? "
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
2021-12-25 23:35:59 +01:00
if len ( store_result ) == 1 :
# Found exactly one key. Store it with that name.
self . result_data = store_result [ 0 ]
QDialog . accept ( self )
return
# Found multiple keys
keys = [ ]
names = [ ]
idx = 1
for key in store_result :
keys . append ( key )
names . append ( self . key_name + " _ " + str ( idx ) )
idx = idx + 1
self . k_full_name_list = names
self . k_full_key_list = keys
2021-12-23 11:29:58 +01:00
QDialog . accept ( self )
2021-12-25 23:35:59 +01:00
return
2021-12-23 11:29:58 +01:00
2021-12-24 14:35:53 +01:00
def accept_ade_dump_passhash ( self ) :
try :
from calibre_plugins . dedrm . adobekey_get_passhash import passhash_keys
keys , names = passhash_keys ( )
except :
errmsg = " Failed to grab PassHash keys from ADE. "
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
# Take the first new key we found.
idx = - 1
new_keys = [ ]
new_names = [ ]
for key in keys :
idx = idx + 1
if key in self . parent . plugin_keys . values ( ) :
continue
new_keys . append ( key )
new_names . append ( names [ idx ] )
if len ( new_keys ) == 0 :
# Okay, we didn't find anything. How do we get rid of the window?
errmsg = " Didn ' t find any PassHash keys in ADE. "
error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
QDialog . reject ( self )
return
# Add new keys to list.
self . k_full_name_list = new_names
self . k_full_key_list = new_keys
QDialog . accept ( self )
return
2021-12-23 11:29:58 +01:00
def accept_win_nook ( self ) :
if len ( self . key_name ) < 4 :
errmsg = " Key name must be at <i>least</i> 4 characters long! "
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
try :
from calibre_plugins . dedrm . ignoblekeyWindowsStore import dump_keys
store_result = dump_keys ( False )
except :
errmsg = " Failed to import from Nook Microsoft Store app. "
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
2021-12-25 23:35:59 +01:00
try :
# Try the Nook Study app
2021-12-23 11:29:58 +01:00
from calibre_plugins . dedrm . ignoblekeyNookStudy import nookkeys
2021-12-25 23:35:59 +01:00
study_result = nookkeys ( )
except :
errmsg = " Failed to import from Nook Study app. "
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
2021-12-23 11:29:58 +01:00
2021-12-25 23:35:59 +01:00
# Add all found keys to a list
keys = [ ]
names = [ ]
idx = 1
for key in store_result :
keys . append ( key )
names . append ( self . key_name + " _nookStore_ " + str ( idx ) )
idx = idx + 1
idx = 1
for key in study_result :
keys . append ( key )
names . append ( self . key_name + " _nookStudy_ " + str ( idx ) )
idx = idx + 1
if len ( keys ) > 0 :
self . k_full_name_list = names
self . k_full_key_list = keys
2021-12-23 11:29:58 +01:00
QDialog . accept ( self )
return
2021-12-25 23:35:59 +01:00
# Okay, we didn't find anything.
2021-12-23 11:29:58 +01:00
errmsg = " Didn ' t find any Nook keys in the Windows app. "
error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
QDialog . reject ( self )
def accept_b64_passhash ( self ) :
if len ( self . key_name ) == 0 or len ( self . cc_number ) == 0 or self . key_name . isspace ( ) or self . cc_number . isspace ( ) :
errmsg = " All fields are required! "
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
if len ( self . key_name ) < 4 :
errmsg = " Key name must be at <i>least</i> 4 characters long! "
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
try :
x = base64 . b64decode ( self . cc_number )
except :
errmsg = " Key data is no valid base64 string! "
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
self . result_data = self . cc_number
QDialog . accept ( self )
def accept_passhash ( self ) :
2013-04-05 18:44:48 +02:00
if len ( self . key_name ) == 0 or len ( self . user_name ) == 0 or len ( self . cc_number ) == 0 or self . key_name . isspace ( ) or self . user_name . isspace ( ) or self . cc_number . isspace ( ) :
2020-09-27 12:54:49 +02:00
errmsg = " All fields are required! "
2013-04-05 18:44:48 +02:00
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
if len ( self . key_name ) < 4 :
2020-09-27 12:54:49 +02:00
errmsg = " Key name must be at <i>least</i> 4 characters long! "
2013-04-05 18:44:48 +02:00
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
2021-12-23 11:29:58 +01:00
try :
from calibre_plugins . dedrm . ignoblekeyGenPassHash import generate_key
self . result_data = generate_key ( self . user_name , self . cc_number )
except :
errmsg = " Key generation failed. "
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
if len ( self . result_data ) == 0 :
errmsg = " Key generation failed. "
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
2013-04-05 18:44:48 +02:00
QDialog . accept ( self )
2021-12-23 11:29:58 +01:00
2013-04-05 18:44:48 +02:00
class AddEReaderDialog ( QDialog ) :
def __init__ ( self , parent = None , ) :
QDialog . __init__ ( self , parent )
self . parent = parent
2020-09-27 12:54:49 +02:00
self . setWindowTitle ( " {0} {1} : Create New eReader Key " . format ( PLUGIN_NAME , PLUGIN_VERSION ) )
2013-04-05 18:44:48 +02:00
layout = QVBoxLayout ( self )
self . setLayout ( layout )
2020-10-14 17:23:49 +02:00
data_group_box = QGroupBox ( " " , self )
2013-04-05 18:44:48 +02:00
layout . addWidget ( data_group_box )
data_group_box_layout = QVBoxLayout ( )
data_group_box . setLayout ( data_group_box_layout )
key_group = QHBoxLayout ( )
data_group_box_layout . addLayout ( key_group )
2020-09-27 12:54:49 +02:00
key_group . addWidget ( QLabel ( " Unique Key Name: " , self ) )
2013-04-05 18:44:48 +02:00
self . key_ledit = QLineEdit ( " " , self )
2020-09-27 12:54:49 +02:00
self . key_ledit . setToolTip ( " <p>Enter an identifying name for this new key. \n It should be something that will help you remember what personal information was used to create it. " )
2013-04-05 18:44:48 +02:00
key_group . addWidget ( self . key_ledit )
name_group = QHBoxLayout ( )
data_group_box_layout . addLayout ( name_group )
2020-09-27 12:54:49 +02:00
name_group . addWidget ( QLabel ( " Your Name: " , self ) )
2020-10-14 17:23:49 +02:00
self . name_ledit = QLineEdit ( " " , self )
2020-09-27 12:54:49 +02:00
self . name_ledit . setToolTip ( " Enter the name for this eReader key, usually the name on your credit card. \n It will only be used to generate this one-time key and won \' t be stored anywhere in calibre or on your computer. \n (ex: Mr Jonathan Q Smith) " )
2013-04-05 18:44:48 +02:00
name_group . addWidget ( self . name_ledit )
2020-09-27 12:54:49 +02:00
name_disclaimer_label = QLabel ( _ ( " (Will not be saved in configuration data) " ) , self )
2013-04-05 18:44:48 +02:00
name_disclaimer_label . setAlignment ( Qt . AlignHCenter )
data_group_box_layout . addWidget ( name_disclaimer_label )
ccn_group = QHBoxLayout ( )
data_group_box_layout . addLayout ( ccn_group )
2020-09-27 12:54:49 +02:00
ccn_group . addWidget ( QLabel ( " Credit Card#: " , self ) )
2020-10-14 17:23:49 +02:00
self . cc_ledit = QLineEdit ( " " , self )
2020-09-27 12:54:49 +02:00
self . cc_ledit . setToolTip ( " <p>Enter the last 8 digits of credit card number for this eReader key. \n They will only be used to generate this one-time key and won \' t be stored anywhere in calibre or on your computer. " )
2013-04-05 18:44:48 +02:00
ccn_group . addWidget ( self . cc_ledit )
ccn_disclaimer_label = QLabel ( _ ( ' (Will not be saved in configuration data) ' ) , self )
ccn_disclaimer_label . setAlignment ( Qt . AlignHCenter )
data_group_box_layout . addWidget ( ccn_disclaimer_label )
layout . addSpacing ( 10 )
self . button_box = QDialogButtonBox ( QDialogButtonBox . Ok | QDialogButtonBox . Cancel )
self . button_box . accepted . connect ( self . accept )
self . button_box . rejected . connect ( self . reject )
layout . addWidget ( self . button_box )
self . resize ( self . sizeHint ( ) )
@property
def key_name ( self ) :
2020-10-14 17:23:49 +02:00
return str ( self . key_ledit . text ( ) ) . strip ( )
2013-04-05 18:44:48 +02:00
@property
def key_value ( self ) :
from calibre_plugins . dedrm . erdr2pml import getuser_key as generate_ereader_key
2020-11-22 16:03:45 +01:00
return codecs . encode ( generate_ereader_key ( self . user_name , self . cc_number ) , ' hex ' )
2013-04-05 18:44:48 +02:00
@property
def user_name ( self ) :
2020-10-14 17:23:49 +02:00
return str ( self . name_ledit . text ( ) ) . strip ( ) . lower ( ) . replace ( ' ' , ' ' )
2013-04-05 18:44:48 +02:00
@property
def cc_number ( self ) :
2020-10-14 17:23:49 +02:00
return str ( self . cc_ledit . text ( ) ) . strip ( ) . replace ( ' ' , ' ' ) . replace ( ' - ' , ' ' )
2013-04-05 18:44:48 +02:00
def accept ( self ) :
if len ( self . key_name ) == 0 or len ( self . user_name ) == 0 or len ( self . cc_number ) == 0 or self . key_name . isspace ( ) or self . user_name . isspace ( ) or self . cc_number . isspace ( ) :
2020-09-27 12:54:49 +02:00
errmsg = " All fields are required! "
2013-04-05 18:44:48 +02:00
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
if not self . cc_number . isdigit ( ) :
2020-09-27 12:54:49 +02:00
errmsg = " Numbers only in the credit card number field! "
2013-04-05 18:44:48 +02:00
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
if len ( self . key_name ) < 4 :
2020-09-27 12:54:49 +02:00
errmsg = " Key name must be at <i>least</i> 4 characters long! "
2013-04-05 18:44:48 +02:00
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
QDialog . accept ( self )
2021-12-25 23:35:59 +01:00
class AddAdeptDialog ( ) :
# We don't actually need to show a dialog, but the wrapper class is expecting a QDialog here.
# Emulate enough methods and parameters so that that works ...
def exec_ ( self ) :
return
def result ( self ) :
return True
@property
def Accepted ( self ) :
return True
2013-04-05 18:44:48 +02:00
def __init__ ( self , parent = None , ) :
2021-12-25 23:35:59 +01:00
2013-04-05 18:44:48 +02:00
self . parent = parent
2021-12-20 21:10:21 +01:00
self . new_keys = [ ]
self . new_names = [ ]
2013-04-05 18:44:48 +02:00
try :
if iswindows or isosx :
from calibre_plugins . dedrm . adobekey import adeptkeys
2021-11-16 11:21:03 +01:00
defaultkeys , defaultnames = adeptkeys ( )
2013-04-24 20:28:20 +02:00
else : # linux
2020-10-14 17:23:49 +02:00
from . wineutils import WineGetKeys
2013-04-05 18:44:48 +02:00
2020-09-27 12:54:49 +02:00
scriptpath = os . path . join ( parent . parent . alfdir , " adobekey.py " )
2021-11-16 11:21:03 +01:00
defaultkeys , defaultnames = WineGetKeys ( scriptpath , " .der " , parent . getwineprefix ( ) )
2013-04-05 18:44:48 +02:00
2021-12-20 21:10:21 +01:00
if sys . version_info [ 0 ] < 3 :
# Python2
import itertools
zip_function = itertools . izip
else :
# Python3
zip_function = zip
for key , name in zip_function ( defaultkeys , defaultnames ) :
if codecs . encode ( key , ' hex ' ) . decode ( " latin-1 " ) in self . parent . plugin_keys . values ( ) :
print ( " Found key ' {0} ' in ADE - already present, skipping. " . format ( name ) )
else :
self . new_keys . append ( key )
self . new_names . append ( name )
2013-04-05 18:44:48 +02:00
except :
2021-12-20 21:10:21 +01:00
print ( " Exception while checking for ADE keys " )
traceback . print_exc ( )
2021-11-16 17:14:03 +01:00
2021-12-20 21:10:21 +01:00
# Check for keys in the DeACSM plugin
try :
2021-11-16 17:14:03 +01:00
key , name = checkForDeACSMkeys ( )
if key is not None :
2021-12-20 21:10:21 +01:00
if codecs . encode ( key , ' hex ' ) . decode ( " latin-1 " ) in self . parent . plugin_keys . values ( ) :
print ( " Found key ' {0} ' in DeACSM - already present, skipping. " . format ( name ) )
else :
# Found new key, add that.
self . new_keys . append ( key )
self . new_names . append ( name )
except :
print ( " Exception while checking for DeACSM keys " )
traceback . print_exc ( )
# Just in case ADE and DeACSM are activated with the same account,
# check the new_keys list for duplicates and remove them, if they exist.
new_keys_2 = [ ]
new_names_2 = [ ]
i = 0
while True :
if i > = len ( self . new_keys ) :
break
if not self . new_keys [ i ] in new_keys_2 :
new_keys_2 . append ( self . new_keys [ i ] )
new_names_2 . append ( self . new_names [ i ] )
2021-12-25 23:35:59 +01:00
i = i + 1
2021-11-16 11:21:03 +01:00
2021-12-25 23:35:59 +01:00
self . k_full_key_list = new_keys_2
self . k_full_name_list = new_names_2
2021-12-20 21:10:21 +01:00
2013-04-05 18:44:48 +02:00
@property
def key_name ( self ) :
2020-10-14 17:23:49 +02:00
return str ( self . key_ledit . text ( ) ) . strip ( )
2013-04-05 18:44:48 +02:00
@property
def key_value ( self ) :
2021-12-20 21:10:21 +01:00
return codecs . encode ( self . new_keys [ 0 ] , ' hex ' ) . decode ( " utf-8 " )
2013-04-05 18:44:48 +02:00
2021-12-25 23:35:59 +01:00
@property
def k_name_list ( self ) :
# If the plugin supports returning multiple keys, return a list of names.
if self . k_full_name_list is not None and self . k_full_key_list is not None :
return self . k_full_name_list
return None
2013-04-05 18:44:48 +02:00
2021-12-25 23:35:59 +01:00
@property
def k_key_list ( self ) :
# If the plugin supports returning multiple keys, return a list of keys.
if self . k_full_name_list is not None and self . k_full_key_list is not None :
return self . k_full_key_list
return None
2013-04-05 18:44:48 +02:00
class AddKindleDialog ( QDialog ) :
def __init__ ( self , parent = None , ) :
QDialog . __init__ ( self , parent )
self . parent = parent
2020-09-27 12:54:49 +02:00
self . setWindowTitle ( " {0} {1} : Getting Default Kindle for Mac/PC Key " . format ( PLUGIN_NAME , PLUGIN_VERSION ) )
2013-04-05 18:44:48 +02:00
layout = QVBoxLayout ( self )
self . setLayout ( layout )
try :
if iswindows or isosx :
from calibre_plugins . dedrm . kindlekey import kindlekeys
defaultkeys = kindlekeys ( )
else : # linux
2020-10-14 17:23:49 +02:00
from . wineutils import WineGetKeys
2013-04-05 18:44:48 +02:00
2020-09-27 12:54:49 +02:00
scriptpath = os . path . join ( parent . parent . alfdir , " kindlekey.py " )
defaultkeys = WineGetKeys ( scriptpath , " .k4i " , parent . getwineprefix ( ) )
2013-04-05 18:44:48 +02:00
self . default_key = defaultkeys [ 0 ]
except :
traceback . print_exc ( )
2020-10-14 17:23:49 +02:00
self . default_key = " "
2013-04-05 18:44:48 +02:00
self . button_box = QDialogButtonBox ( QDialogButtonBox . Ok | QDialogButtonBox . Cancel )
if len ( self . default_key ) > 0 :
2020-10-14 17:23:49 +02:00
data_group_box = QGroupBox ( " " , self )
2013-04-05 18:44:48 +02:00
layout . addWidget ( data_group_box )
data_group_box_layout = QVBoxLayout ( )
data_group_box . setLayout ( data_group_box_layout )
key_group = QHBoxLayout ( )
data_group_box_layout . addLayout ( key_group )
2020-09-27 12:54:49 +02:00
key_group . addWidget ( QLabel ( " Unique Key Name: " , self ) )
self . key_ledit = QLineEdit ( " default_key " , self )
self . key_ledit . setToolTip ( " <p>Enter an identifying name for the current default Kindle for Mac/PC key. " )
2013-04-05 18:44:48 +02:00
key_group . addWidget ( self . key_ledit )
2015-07-29 19:11:19 +02:00
2013-04-05 18:44:48 +02:00
self . button_box . accepted . connect ( self . accept )
else :
2020-09-27 12:54:49 +02:00
default_key_error = QLabel ( " The default encryption key for Kindle for Mac/PC could not be found. " , self )
2013-04-05 18:44:48 +02:00
default_key_error . setAlignment ( Qt . AlignHCenter )
layout . addWidget ( default_key_error )
2020-09-26 22:22:47 +02:00
2015-07-29 19:11:19 +02:00
# if no default, both buttons do the same
2013-04-05 18:44:48 +02:00
self . button_box . accepted . connect ( self . reject )
self . button_box . rejected . connect ( self . reject )
layout . addWidget ( self . button_box )
self . resize ( self . sizeHint ( ) )
@property
def key_name ( self ) :
2020-10-14 17:23:49 +02:00
return str ( self . key_ledit . text ( ) ) . strip ( )
2013-04-05 18:44:48 +02:00
@property
def key_value ( self ) :
return self . default_key
def accept ( self ) :
if len ( self . key_name ) == 0 or self . key_name . isspace ( ) :
2020-09-27 12:54:49 +02:00
errmsg = " All fields are required! "
2013-04-05 18:44:48 +02:00
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
if len ( self . key_name ) < 4 :
2020-09-27 12:54:49 +02:00
errmsg = " Key name must be at <i>least</i> 4 characters long! "
2013-04-05 18:44:48 +02:00
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
QDialog . accept ( self )
class AddSerialDialog ( QDialog ) :
def __init__ ( self , parent = None , ) :
QDialog . __init__ ( self , parent )
self . parent = parent
2020-09-27 12:54:49 +02:00
self . setWindowTitle ( " {0} {1} : Add New EInk Kindle Serial Number " . format ( PLUGIN_NAME , PLUGIN_VERSION ) )
2013-04-05 18:44:48 +02:00
layout = QVBoxLayout ( self )
self . setLayout ( layout )
2020-10-14 17:23:49 +02:00
data_group_box = QGroupBox ( " " , self )
2013-04-05 18:44:48 +02:00
layout . addWidget ( data_group_box )
data_group_box_layout = QVBoxLayout ( )
data_group_box . setLayout ( data_group_box_layout )
key_group = QHBoxLayout ( )
data_group_box_layout . addLayout ( key_group )
2020-09-27 12:54:49 +02:00
key_group . addWidget ( QLabel ( " EInk Kindle Serial Number: " , self ) )
2013-04-05 18:44:48 +02:00
self . key_ledit = QLineEdit ( " " , self )
2020-09-27 12:54:49 +02:00
self . key_ledit . setToolTip ( " Enter an eInk Kindle serial number. EInk Kindle serial numbers are 16 characters long and usually start with a ' B ' or a ' 9 ' . Kindle Serial Numbers are case-sensitive, so be sure to enter the upper and lower case letters unchanged. " )
2013-04-05 18:44:48 +02:00
key_group . addWidget ( self . key_ledit )
self . button_box = QDialogButtonBox ( QDialogButtonBox . Ok | QDialogButtonBox . Cancel )
self . button_box . accepted . connect ( self . accept )
self . button_box . rejected . connect ( self . reject )
layout . addWidget ( self . button_box )
self . resize ( self . sizeHint ( ) )
@property
def key_name ( self ) :
2020-10-14 17:23:49 +02:00
return str ( self . key_ledit . text ( ) ) . strip ( )
2013-04-05 18:44:48 +02:00
@property
def key_value ( self ) :
2020-10-14 17:23:49 +02:00
return str ( self . key_ledit . text ( ) ) . replace ( ' ' , ' ' )
2013-04-05 18:44:48 +02:00
def accept ( self ) :
if len ( self . key_name ) == 0 or self . key_name . isspace ( ) :
2020-09-27 12:54:49 +02:00
errmsg = " Please enter an eInk Kindle Serial Number or click Cancel in the dialog. "
2013-04-05 18:44:48 +02:00
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
if len ( self . key_name ) != 16 :
2020-09-27 12:54:49 +02:00
errmsg = " EInk Kindle Serial Numbers must be 16 characters long. This is {0:d} characters long. " . format ( len ( self . key_name ) )
2013-04-05 18:44:48 +02:00
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
QDialog . accept ( self )
2015-07-29 19:11:19 +02:00
class AddAndroidDialog ( QDialog ) :
2015-03-17 18:49:30 +01:00
def __init__ ( self , parent = None , ) :
2015-07-29 19:11:19 +02:00
2015-03-17 18:49:30 +01:00
QDialog . __init__ ( self , parent )
self . parent = parent
2020-09-27 12:54:49 +02:00
self . setWindowTitle ( " {0} {1} : Add new Kindle for Android Key " . format ( PLUGIN_NAME , PLUGIN_VERSION ) )
2015-03-17 18:49:30 +01:00
layout = QVBoxLayout ( self )
self . setLayout ( layout )
2015-07-29 19:11:19 +02:00
self . button_box = QDialogButtonBox ( QDialogButtonBox . Ok | QDialogButtonBox . Cancel )
2015-03-17 18:49:30 +01:00
2020-10-14 17:23:49 +02:00
data_group_box = QGroupBox ( " " , self )
2015-03-17 18:49:30 +01:00
layout . addWidget ( data_group_box )
data_group_box_layout = QVBoxLayout ( )
data_group_box . setLayout ( data_group_box_layout )
2015-07-29 19:11:19 +02:00
file_group = QHBoxLayout ( )
data_group_box_layout . addLayout ( file_group )
2020-09-27 12:54:49 +02:00
add_btn = QPushButton ( " Choose Backup File " , self )
add_btn . setToolTip ( " Import Kindle for Android backup file. " )
2015-07-29 19:11:19 +02:00
add_btn . clicked . connect ( self . get_android_file )
file_group . addWidget ( add_btn )
2020-10-14 17:23:49 +02:00
self . selected_file_name = QLabel ( " " , self )
2015-07-29 19:11:19 +02:00
self . selected_file_name . setAlignment ( Qt . AlignHCenter )
file_group . addWidget ( self . selected_file_name )
2020-09-26 22:22:47 +02:00
2015-03-17 18:49:30 +01:00
key_group = QHBoxLayout ( )
data_group_box_layout . addLayout ( key_group )
2020-09-27 12:54:49 +02:00
key_group . addWidget ( QLabel ( " Unique Key Name: " , self ) )
2020-10-14 17:23:49 +02:00
self . key_ledit = QLineEdit ( " " , self )
2020-09-27 12:54:49 +02:00
self . key_ledit . setToolTip ( " <p>Enter an identifying name for the Android for Kindle key. " )
2015-03-17 18:49:30 +01:00
key_group . addWidget ( self . key_ledit )
2015-07-29 19:11:19 +02:00
#key_label = QLabel(_(''), self)
#key_label.setAlignment(Qt.AlignHCenter)
#data_group_box_layout.addWidget(key_label)
2020-09-26 22:22:47 +02:00
2015-03-17 18:49:30 +01:00
self . button_box . accepted . connect ( self . accept )
self . button_box . rejected . connect ( self . reject )
layout . addWidget ( self . button_box )
self . resize ( self . sizeHint ( ) )
@property
def key_name ( self ) :
2020-10-14 17:23:49 +02:00
return str ( self . key_ledit . text ( ) ) . strip ( )
2015-03-17 18:49:30 +01:00
2015-07-29 19:11:19 +02:00
@property
def file_name ( self ) :
2020-10-14 17:23:49 +02:00
return str ( self . selected_file_name . text ( ) ) . strip ( )
2015-07-29 19:11:19 +02:00
2015-03-17 18:49:30 +01:00
@property
def key_value ( self ) :
2015-07-29 19:11:19 +02:00
return self . serials_from_file
2020-09-26 22:22:47 +02:00
2015-07-29 19:11:19 +02:00
def get_android_file ( self ) :
2020-09-27 12:54:49 +02:00
unique_dlg_name = PLUGIN_NAME + " Import Kindle for Android backup file " #takes care of automatically remembering last directory
caption = " Select Kindle for Android backup file to add "
filters = [ ( " Kindle for Android backup files " , [ ' db ' , ' ab ' , ' xml ' ] ) ]
2015-07-29 19:11:19 +02:00
files = choose_files ( self , unique_dlg_name , caption , filters , all_files = False )
self . serials_from_file = [ ]
2020-10-14 17:23:49 +02:00
file_name = " "
2015-07-29 19:11:19 +02:00
if files :
# find the first selected file that yields some serial numbers
for filename in files :
fpath = os . path . join ( config_dir , filename )
self . filename = os . path . basename ( filename )
file_serials = androidkindlekey . get_serials ( fpath )
if len ( file_serials ) > 0 :
file_name = os . path . basename ( self . filename )
self . serials_from_file . extend ( file_serials )
self . selected_file_name . setText ( file_name )
2020-09-26 22:22:47 +02:00
2015-03-17 18:49:30 +01:00
def accept ( self ) :
2015-07-29 19:11:19 +02:00
if len ( self . file_name ) == 0 or len ( self . key_value ) == 0 :
2020-09-27 12:54:49 +02:00
errmsg = " Please choose a Kindle for Android backup file. "
2015-07-29 19:11:19 +02:00
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
2015-03-17 18:49:30 +01:00
if len ( self . key_name ) == 0 or self . key_name . isspace ( ) :
2020-09-27 12:54:49 +02:00
errmsg = " Please enter a key name. "
2015-07-29 19:11:19 +02:00
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
if len ( self . key_name ) < 4 :
2020-09-27 12:54:49 +02:00
errmsg = " Key name must be at <i>least</i> 4 characters long! "
2015-03-17 18:49:30 +01:00
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
QDialog . accept ( self )
2013-04-05 18:44:48 +02:00
class AddPIDDialog ( QDialog ) :
def __init__ ( self , parent = None , ) :
QDialog . __init__ ( self , parent )
self . parent = parent
2020-09-27 12:54:49 +02:00
self . setWindowTitle ( " {0} {1} : Add New Mobipocket PID " . format ( PLUGIN_NAME , PLUGIN_VERSION ) )
2013-04-05 18:44:48 +02:00
layout = QVBoxLayout ( self )
self . setLayout ( layout )
2020-10-14 17:23:49 +02:00
data_group_box = QGroupBox ( " " , self )
2013-04-05 18:44:48 +02:00
layout . addWidget ( data_group_box )
data_group_box_layout = QVBoxLayout ( )
data_group_box . setLayout ( data_group_box_layout )
key_group = QHBoxLayout ( )
data_group_box_layout . addLayout ( key_group )
2020-09-27 12:54:49 +02:00
key_group . addWidget ( QLabel ( " PID: " , self ) )
2013-04-05 18:44:48 +02:00
self . key_ledit = QLineEdit ( " " , self )
2020-09-27 12:54:49 +02:00
self . key_ledit . setToolTip ( " Enter a Mobipocket PID. Mobipocket PIDs are 8 or 10 characters long. Mobipocket PIDs are case-sensitive, so be sure to enter the upper and lower case letters unchanged. " )
2013-04-05 18:44:48 +02:00
key_group . addWidget ( self . key_ledit )
self . button_box = QDialogButtonBox ( QDialogButtonBox . Ok | QDialogButtonBox . Cancel )
self . button_box . accepted . connect ( self . accept )
self . button_box . rejected . connect ( self . reject )
layout . addWidget ( self . button_box )
self . resize ( self . sizeHint ( ) )
@property
def key_name ( self ) :
2020-10-14 17:23:49 +02:00
return str ( self . key_ledit . text ( ) ) . strip ( )
2013-04-05 18:44:48 +02:00
@property
def key_value ( self ) :
2020-10-14 17:23:49 +02:00
return str ( self . key_ledit . text ( ) ) . strip ( )
2013-04-05 18:44:48 +02:00
def accept ( self ) :
if len ( self . key_name ) == 0 or self . key_name . isspace ( ) :
2020-09-27 12:54:49 +02:00
errmsg = " Please enter a Mobipocket PID or click Cancel in the dialog. "
2013-04-05 18:44:48 +02:00
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
if len ( self . key_name ) != 8 and len ( self . key_name ) != 10 :
2020-09-27 12:54:49 +02:00
errmsg = " Mobipocket PIDs must be 8 or 10 characters long. This is {0:d} characters long. " . format ( len ( self . key_name ) )
2013-04-05 18:44:48 +02:00
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
QDialog . accept ( self )
2021-11-17 21:53:24 +01:00
class AddLCPKeyDialog ( QDialog ) :
def __init__ ( self , parent = None , ) :
QDialog . __init__ ( self , parent )
self . parent = parent
self . setWindowTitle ( " {0} {1} : Add new Readium LCP passphrase " . format ( PLUGIN_NAME , PLUGIN_VERSION ) )
layout = QVBoxLayout ( self )
self . setLayout ( layout )
data_group_box = QGroupBox ( " " , self )
layout . addWidget ( data_group_box )
data_group_box_layout = QVBoxLayout ( )
data_group_box . setLayout ( data_group_box_layout )
key_group = QHBoxLayout ( )
data_group_box_layout . addLayout ( key_group )
key_group . addWidget ( QLabel ( " Readium LCP passphrase: " , self ) )
self . key_ledit = QLineEdit ( " " , self )
self . key_ledit . setToolTip ( " Enter your Readium LCP passphrase " )
key_group . addWidget ( self . key_ledit )
self . button_box = QDialogButtonBox ( QDialogButtonBox . Ok | QDialogButtonBox . Cancel )
self . button_box . accepted . connect ( self . accept )
self . button_box . rejected . connect ( self . reject )
layout . addWidget ( self . button_box )
self . resize ( self . sizeHint ( ) )
@property
def key_name ( self ) :
return None
@property
def key_value ( self ) :
return str ( self . key_ledit . text ( ) )
def accept ( self ) :
if len ( self . key_value ) == 0 or self . key_value . isspace ( ) :
errmsg = " Please enter your LCP passphrase or click Cancel in the dialog. "
return error_dialog ( None , " {0} {1} " . format ( PLUGIN_NAME , PLUGIN_VERSION ) , errmsg , show = True , show_copy_button = False )
QDialog . accept ( self )