mirror of
https://github.com/Leseratte10/acsm-calibre-plugin.git
synced 2024-12-22 17:29:56 +06:00
Support converting anonymous auth to new AdobeID
This commit is contained in:
parent
cc37506762
commit
732ed1cf3e
@ -21,9 +21,9 @@ Once that's done, download an ACSM file from Adobe's test library and see if you
|
||||
IMPORTANT:
|
||||
|
||||
- I would suggest creating a new dummy AdobeID to use for Calibre so just in case Adobe detects this and bans you, you don't lose your main AdobeID.
|
||||
- Combined with that I suggest importing the DER file into the DeDRM plugin to make sure that losing your AdobeID doesn't also mean you'll lose access to all your eBooks.
|
||||
- Combined with that I suggest using the DeDRM plugin to make sure that losing your AdobeID doesn't also mean you'll lose access to all your eBooks.
|
||||
- If you use an anonymous authorization, make sure you make backups of the activation data.
|
||||
- If you use an anonymous authorization and you end up getting an eBook with the new Adobe DRM (version 3 or higher), there might be no way for you to access that book at all, as right now there's no way to export an existing authorization from the plugin into ADE.
|
||||
- If you use an anonymous authorization, you have the ability to copy that authorization into an AdobeID account at a later time (by clicking "Connect anonymous auth to ADE account"). This is useful if you have books linked to your authorization that you want to read elsewhere. Same restrictions as with ADE apply - you can only do this ONCE per AdobeID, and only if the AdobeID hasn't been in use elsewhere yet.
|
||||
- This software is not approved by Adobe. I am not responsible if Adobe detects that you're using nonstandard software and bans your account. Do not complain to me if Adobe bans your main ADE account - you have been warned.
|
||||
|
||||
## Returning books
|
||||
|
@ -31,7 +31,8 @@
|
||||
# add useful error message for ACSMs with nonstandard download type.
|
||||
# Currently in development:
|
||||
# Add support for anonymous authorizations, add support for other ID providers,
|
||||
# fix ACSM files from Google Play books (no metadata node).
|
||||
# fix ACSM files from Google Play books (no metadata node),
|
||||
# allow converting an anonymous auth to an AdobeID auth.
|
||||
|
||||
PLUGIN_NAME = "DeACSM"
|
||||
PLUGIN_VERSION_TUPLE = (0, 0, 14)
|
||||
|
@ -102,6 +102,15 @@ class ConfigWidget(QWidget):
|
||||
ua_group_box_layout.addWidget(self.button_import_activation)
|
||||
|
||||
else:
|
||||
|
||||
if mail is None:
|
||||
# Current auth is anon auth. Offer to link to account.
|
||||
|
||||
self.button_convert_anon_to_account = QtGui.QPushButton(self)
|
||||
self.button_convert_anon_to_account.setText(_("Connect anonymous auth to ADE account"))
|
||||
self.button_convert_anon_to_account.clicked.connect(self.convert_anon_to_account)
|
||||
ua_group_box_layout.addWidget(self.button_convert_anon_to_account)
|
||||
|
||||
self.button_switch_ade_version = QtGui.QPushButton(self)
|
||||
self.button_switch_ade_version.setText(_("Change ADE version"))
|
||||
self.button_switch_ade_version.clicked.connect(self.switch_ade_version)
|
||||
@ -185,6 +194,10 @@ class ConfigWidget(QWidget):
|
||||
self.button_import_LinuxWineADE.setEnabled(activated)
|
||||
else:
|
||||
self.button_switch_ade_version.setEnabled(False)
|
||||
try:
|
||||
self.button_convert_anon_to_account.setEnabled(False)
|
||||
except:
|
||||
pass
|
||||
self.button_export_key.setEnabled(False)
|
||||
self.button_export_activation.setEnabled(False)
|
||||
self.button_rented_books.setEnabled(False)
|
||||
@ -274,6 +287,8 @@ class ConfigWidget(QWidget):
|
||||
self.button_switch_ade_version.setEnabled(False)
|
||||
except:
|
||||
pass
|
||||
if ade_mail is None:
|
||||
self.button_convert_anon_to_account.setEnabled(False)
|
||||
self.button_export_activation.setEnabled(False)
|
||||
self.button_export_key.setEnabled(False)
|
||||
self.lblAccInfo.setText("Authorization deleted.\nClose and re-open this window to add a new authorization.")
|
||||
@ -461,6 +476,10 @@ class ConfigWidget(QWidget):
|
||||
|
||||
self.button_link_account.setEnabled(not activated)
|
||||
self.button_anon_auth.setEnabled(not activated)
|
||||
try:
|
||||
self.button_convert_anon_to_account.setEnabled(ade_mail is None)
|
||||
except:
|
||||
pass
|
||||
self.button_import_activation.setEnabled(not activated)
|
||||
self.button_import_LinuxWineADE.setEnabled(not activated)
|
||||
self.button_export_key.setEnabled(activated)
|
||||
@ -494,6 +513,10 @@ class ConfigWidget(QWidget):
|
||||
|
||||
self.button_link_account.setEnabled(not activated)
|
||||
self.button_anon_auth.setEnabled(not activated)
|
||||
try:
|
||||
self.button_convert_anon_to_account.setEnabled(ade_mail is None)
|
||||
except:
|
||||
pass
|
||||
self.button_import_activation.setEnabled(not activated)
|
||||
self.button_import_WinADE.setEnabled(not activated)
|
||||
self.button_export_key.setEnabled(activated)
|
||||
@ -532,6 +555,10 @@ class ConfigWidget(QWidget):
|
||||
|
||||
self.button_link_account.setEnabled(not activated)
|
||||
self.button_anon_auth.setEnabled(not activated)
|
||||
try:
|
||||
self.button_convert_anon_to_account.setEnabled(ade_mail is None)
|
||||
except:
|
||||
pass
|
||||
self.button_import_activation.setEnabled(not activated)
|
||||
self.button_import_MacADE.setEnabled(not activated)
|
||||
self.button_export_key.setEnabled(activated)
|
||||
@ -597,6 +624,10 @@ class ConfigWidget(QWidget):
|
||||
|
||||
self.button_link_account.setEnabled(not activated)
|
||||
self.button_anon_auth.setEnabled(not activated)
|
||||
try:
|
||||
self.button_convert_anon_to_account.setEnabled(ade_mail is None)
|
||||
except:
|
||||
pass
|
||||
self.button_import_activation.setEnabled(not activated)
|
||||
self.button_export_key.setEnabled(activated)
|
||||
self.button_export_activation.setEnabled(activated)
|
||||
@ -794,6 +825,10 @@ class ConfigWidget(QWidget):
|
||||
|
||||
self.button_link_account.setEnabled(not activated)
|
||||
self.button_anon_auth.setEnabled(not activated)
|
||||
try:
|
||||
self.button_convert_anon_to_account.setEnabled(mail is None)
|
||||
except:
|
||||
pass
|
||||
self.button_import_activation.setEnabled(not activated)
|
||||
self.button_export_key.setEnabled(activated)
|
||||
self.button_export_activation.setEnabled(activated)
|
||||
@ -808,6 +843,75 @@ class ConfigWidget(QWidget):
|
||||
|
||||
info_dialog(None, "Done", "Authorized to anonymous account.", show=True, show_copy_button=False)
|
||||
|
||||
|
||||
def convert_anon_to_account(self):
|
||||
try:
|
||||
from calibre_plugins.deacsm.libadobe import createDeviceKeyFile, update_account_path
|
||||
from calibre_plugins.deacsm.libadobeAccount import convertAnonAuthToAccount
|
||||
except:
|
||||
try:
|
||||
from libadobe import createDeviceKeyFile, update_account_path
|
||||
from libadobeAccount import convertAnonAuthToAccount
|
||||
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"])
|
||||
|
||||
# This MUST only be called on anonymous accounts.
|
||||
# The button should be disabled if that's not the case, but just to make sure ...
|
||||
info_string, activated, mail = self.get_account_info()
|
||||
if (not activated):
|
||||
return
|
||||
|
||||
if (mail is not None):
|
||||
return
|
||||
|
||||
msg = "You are about to link your anonymous authorization to an AdobeID. "
|
||||
msg += "This only works ONCE for each AdobeID. The anonymous authorization will then "
|
||||
msg += "permanently be connected to your AdobeID. This is intended for cases where the user "
|
||||
msg += "has started with an anonymous authorization, and then creates a fresh AdobeID later "
|
||||
msg += "and doesn't want to lose his books.\n\n"
|
||||
msg += "Only continue if you fully understand this."
|
||||
|
||||
|
||||
warning_dialog(None, "Warning", msg, show=True, show_copy_button=False)
|
||||
|
||||
|
||||
mail, ok = QInputDialog.getText(self, "Authorizing ADE account", "Please enter mail address")
|
||||
|
||||
if (not ok or mail is None or len(mail) == 0):
|
||||
return
|
||||
|
||||
passwd, ok = QInputDialog.getText(self, "Authorizing ADE account", "Please enter password", QLineEdit.Password)
|
||||
|
||||
if (not ok or passwd is None or len(passwd) == 0):
|
||||
return
|
||||
|
||||
success, message = convertAnonAuthToAccount(mail, passwd)
|
||||
|
||||
if (success):
|
||||
# update display
|
||||
info_string, activated, mail = self.get_account_info()
|
||||
self.lblAccInfo.setText(info_string)
|
||||
|
||||
try:
|
||||
self.button_convert_anon_to_account.setEnabled(mail is None)
|
||||
except:
|
||||
pass
|
||||
|
||||
self.resize(self.sizeHint())
|
||||
|
||||
return info_dialog(None, "Done", "Successfully converted anonynmous authentication to AdobeID", show=True, show_copy_button=False)
|
||||
|
||||
else:
|
||||
err_msg = "Could not link anonymous authentication to AdobeID.\n"
|
||||
err_msg += "This only works with a fresh AdobeID that has never been linked to any ADE install."
|
||||
|
||||
return error_dialog(None, "ADE activation failed", err_msg, det_msg=str(message), show=True, show_copy_button=True)
|
||||
|
||||
|
||||
|
||||
def link_account(self):
|
||||
|
||||
try:
|
||||
@ -906,6 +1010,10 @@ class ConfigWidget(QWidget):
|
||||
|
||||
self.button_link_account.setEnabled(not activated)
|
||||
self.button_anon_auth.setEnabled(not activated)
|
||||
try:
|
||||
self.button_convert_anon_to_account.setEnabled(mail is None)
|
||||
except:
|
||||
pass
|
||||
self.button_import_activation.setEnabled(not activated)
|
||||
self.button_export_key.setEnabled(activated)
|
||||
self.button_export_activation.setEnabled(activated)
|
||||
|
@ -215,14 +215,7 @@ def createUser(useVersionIndex: int = 0, authCert = None):
|
||||
|
||||
return True, "Done"
|
||||
|
||||
|
||||
def buildSignInRequest(type: str, username: str, password: str, authenticationCertificate: str):
|
||||
NSMAP = { "adept" : "http://ns.adobe.com/adept" }
|
||||
etree.register_namespace("adept", NSMAP["adept"])
|
||||
|
||||
root = etree.Element(etree.QName(NSMAP["adept"], "signIn"))
|
||||
root.set("method", type)
|
||||
|
||||
def encryptLoginCredentials(username: str, password: str, authenticationCertificate: str):
|
||||
f = open(get_devkey_path(), "rb")
|
||||
devkey_bytes = f.read()
|
||||
f.close()
|
||||
@ -248,6 +241,43 @@ def buildSignInRequest(type: str, username: str, password: str, authenticationCe
|
||||
cipherAC = PKCS1_v1_5.new(rsakey)
|
||||
crypted_msg = cipherAC.encrypt(bytes(ar))
|
||||
|
||||
return crypted_msg
|
||||
|
||||
|
||||
def buildSignInRequestForAnonAuthConvert(username: str, password: str, authenticationCertificate: str):
|
||||
NSMAP = { "adept" : "http://ns.adobe.com/adept" }
|
||||
etree.register_namespace("adept", NSMAP["adept"])
|
||||
|
||||
root = etree.Element(etree.QName(NSMAP["adept"], "signIn"))
|
||||
root.set("method", "AdobeID")
|
||||
|
||||
crypted_msg = encryptLoginCredentials(username, password, authenticationCertificate)
|
||||
|
||||
etree.SubElement(root, etree.QName(NSMAP["adept"], "signInData")).text = base64.b64encode(crypted_msg)
|
||||
|
||||
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
|
||||
except:
|
||||
return None
|
||||
|
||||
etree.SubElement(root, etree.QName(NSMAP["adept"], "user")).text = user_uuid
|
||||
signature = sign_node(root)
|
||||
etree.SubElement(root, etree.QName(NSMAP["adept"], "signature")).text = signature
|
||||
|
||||
return "<?xml version=\"1.0\"?>\n" + etree.tostring(root, encoding="utf-8", pretty_print=True, xml_declaration=False).decode("latin-1")
|
||||
|
||||
|
||||
def buildSignInRequest(type: str, username: str, password: str, authenticationCertificate: str):
|
||||
NSMAP = { "adept" : "http://ns.adobe.com/adept" }
|
||||
etree.register_namespace("adept", NSMAP["adept"])
|
||||
|
||||
root = etree.Element(etree.QName(NSMAP["adept"], "signIn"))
|
||||
root.set("method", type)
|
||||
|
||||
crypted_msg = encryptLoginCredentials(username, password, authenticationCertificate)
|
||||
|
||||
etree.SubElement(root, etree.QName(NSMAP["adept"], "signInData")).text = base64.b64encode(crypted_msg)
|
||||
|
||||
# Generate Auth key and License Key
|
||||
@ -272,6 +302,74 @@ def buildSignInRequest(type: str, username: str, password: str, authenticationCe
|
||||
return "<?xml version=\"1.0\"?>\n" + etree.tostring(root, encoding="utf-8", pretty_print=True, xml_declaration=False).decode("latin-1")
|
||||
|
||||
|
||||
def convertAnonAuthToAccount(username: str, passwd: str):
|
||||
# If you have an anonymous authorization, you can convert that to an AdobeID.
|
||||
# Important: You can only do this ONCE for each AdobeID.
|
||||
# The AdobeID you are using for this must not be connected to any ADE install.
|
||||
|
||||
# This is intended for cases where people install ADE, use an anonymous auth,
|
||||
# buy a couple books, and then decide to get a fresh AdobeID.
|
||||
|
||||
# Get authenticationCertificate
|
||||
try:
|
||||
activationxml = etree.parse(get_activation_xml_path())
|
||||
adNS = lambda tag: '{%s}%s' % ('http://ns.adobe.com/adept', tag)
|
||||
authenticationCertificate = activationxml.find("./%s/%s" % (adNS("activationServiceInfo"), adNS("authenticationCertificate"))).text
|
||||
except:
|
||||
return False, "Missing authenticationCertificate"
|
||||
|
||||
if authenticationCertificate == "":
|
||||
return False, "Empty authenticationCertificate"
|
||||
|
||||
linkRequest = buildSignInRequestForAnonAuthConvert(username, passwd, authenticationCertificate)
|
||||
signInURL = activationxml.find("./%s/%s" % (adNS("activationServiceInfo"), adNS("authURL"))).text + "/AddSignInDirect"
|
||||
linkResponse = sendRequestDocu(linkRequest, signInURL)
|
||||
|
||||
try:
|
||||
credentialsXML = etree.fromstring(linkResponse)
|
||||
|
||||
if (credentialsXML.tag == adNS("error")):
|
||||
err = credentialsXML.get("data")
|
||||
err_parts = err.split(' ')
|
||||
if err_parts[0] == "E_AUTH_USER_ALREADY_REGISTERED":
|
||||
try:
|
||||
return False, "Can't link anon auth " + err_parts[2] + " to account, account already has user ID " + err_parts[3]
|
||||
except:
|
||||
pass
|
||||
|
||||
return False, "Can't link anon auth to account: " + err
|
||||
|
||||
elif (credentialsXML.tag != adNS("success")):
|
||||
return False, "Invalid main tag " + credentialsXML.tag
|
||||
except:
|
||||
return False, "Invalid response to login request"
|
||||
|
||||
|
||||
# If we end up here, the account linking was successful. Now we just need to update the activation.xml accordingly.
|
||||
|
||||
activationxml = etree.parse(get_activation_xml_path())
|
||||
adNS = lambda tag: '{%s}%s' % ('http://ns.adobe.com/adept', tag)
|
||||
cred_node = activationxml.find("./%s" % (adNS("credentials")))
|
||||
|
||||
|
||||
NSMAP = { "adept" : "http://ns.adobe.com/adept" }
|
||||
tmp_node = etree.SubElement(cred_node, etree.QName(NSMAP["adept"], "username"))
|
||||
|
||||
# Adobe / ADE only supports this account linking for AdobeID accounts, not for any Vendor IDs.
|
||||
tmp_node.set("method", "AdobeID")
|
||||
tmp_node.text = username
|
||||
|
||||
# Write to file
|
||||
f = open(get_activation_xml_path(), "w")
|
||||
f.write("<?xml version=\"1.0\"?>\n")
|
||||
f.write(etree.tostring(activationxml, encoding="utf-8", pretty_print=True, xml_declaration=False).decode("latin-1"))
|
||||
f.close()
|
||||
|
||||
|
||||
return True, "Account linking successful"
|
||||
|
||||
|
||||
|
||||
|
||||
def signIn(account_type: str, username: str, passwd: str):
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user