Compare commits

..

20 Commits

Author SHA1 Message Date
Apprentice Harper
00a5c4e1d1 Updated obok plugin readme 2015-10-05 07:49:48 +01:00
apprenticeharper
4ea0d81144 Change to unicode strings to fix stand-alone character encoding problems 2015-09-30 07:39:29 +01:00
Apprentice Harper
b1cccf4b25 Merge pull request #39 from norbusan/obok_linux
improvements in the obok device handling
2015-09-21 07:40:54 +01:00
Norbert Preining
fe6074949b obok.py: first try device, and only if that fails fall back to Desktop progs 2015-09-18 09:10:06 +09:00
Norbert Preining
2db7ee8894 obok_plugin:action.py - get serial from device if possible 2015-09-18 08:58:01 +09:00
Norbert Preining
93d8758462 get device path from calibre, and allow device usage on all platforms 2015-09-16 23:01:29 +09:00
Norbert Preining
f97bc078db add support for linux via device serials and reading from device 2015-09-14 14:12:22 +09:00
apprenticeharper
2e96db6cdc More changes to the obok cli interface for character encodings 2015-09-08 07:52:06 +01:00
apprenticeharper
0d530c0c46 Add encryption fixes from other scripts (SafeUnbuffered). 2015-09-07 08:12:45 +01:00
apprenticeharper
488924d443 Fix for kobo users who haven't yet bought a book. 2015-09-07 07:52:23 +01:00
apprenticeharper
07485be2c0 Add transparency to the obok logo 2015-09-03 08:06:33 +01:00
apprenticeharper
e5e269fbae Fixes for android key extraction 2015-09-03 07:51:10 +01:00
apprenticeharper
d54dc38c2d Fix for Kobo Desktop 3.17 2015-09-03 07:49:09 +01:00
apprenticeharper
3c322f3695 Second and last step to fix capitalisation issue 2015-09-01 07:08:46 +01:00
apprenticeharper
c112c28f58 First step to fix capitalisation issue 2015-09-01 07:06:13 +01:00
apprenticeharper
b8606cd182 Merge pull request #32 from eli-schwartz/master
Linux: Allow using ~ when specifying a wineprefix.
2015-09-01 06:57:48 +01:00
Eli Schwartz
f2190a6755 Linux: Allow using ~ when specifying a wineprefix. 2015-08-12 17:39:50 -04:00
apprenticeharper
33d8a63f61 Fix for plugin bug introduced in 6.3.1 for Kindle for PC 2015-08-12 18:35:21 +01:00
apprenticeharper
91b22c18c4 Update kobo plugin to 3.1.3 with Portuguese and Arabic translations. Include .mo files in git. 2015-08-05 06:59:01 +01:00
apprenticeharper
3317dc7330 Fixed plugin help file and updated readmes 2015-08-04 07:18:33 +01:00
39 changed files with 1205 additions and 351 deletions

1
.gitignore vendored
View File

@@ -37,7 +37,6 @@ nosetests.xml
coverage.xml
# Translations
*.mo
*.pot
# Django stuff:

View File

@@ -24,7 +24,7 @@
<key>CFBundleExecutable</key>
<string>droplet</string>
<key>CFBundleGetInfoString</key>
<string>DeDRM AppleScript 6.3.1 Written 20102015 by Apprentice Alf et al.</string>
<string>DeDRM AppleScript 6.3.4 Written 20102015 by Apprentice Alf et al.</string>
<key>CFBundleIconFile</key>
<string>DeDRM</string>
<key>CFBundleIdentifier</key>
@@ -36,7 +36,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>6.3.1</string>
<string>6.3.4</string>
<key>CFBundleSignature</key>
<string>dplt</string>
<key>LSRequiresCarbon</key>

View File

@@ -0,0 +1,61 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Managing Kindle for Android Keys</title>
<style type="text/css">
span.version {font-size: 50%}
span.bold {font-weight: bold}
h3 {margin-bottom: 0}
p {margin-top: 0}
li {margin-top: 0.5em}
</style>
</head>
<body>
<h1>Managing Kindle for Android Keys</h1>
<p>Amazon's Kindle for Android application uses an internal key equivalent to an eInk Kindle's serial number. Extracting that key is a little tricky, but worth it, as it then allows the DRM to be removed from any Kindle ebooks that have been downloaded to that Android device.</p>
<p>Please note that it is not currently known whether the same applies to the Kindle application on the Kindle Fire and Fire HD.</p>
<h3>Getting the Kindle for Android backup file</h3>
<p>Obtain and install adb (Android Debug Bridge) on your computer. Details of how to do this are beyond the scope of this help file, but there are plenty of on-line guides.</p>
<p>Enable developer mode on your Android device. Again, look for an on-line guide for your device.</p>
<p>Once you have adb installed and your device in developer mode, connect your device to your computer with a USB cable and then open up a command line (Terminal on Mac OS X and cmd.exe on Windows) and enter "adb backup com.amazon.kindle" (without the quotation marks!) and press return. A file "backup.ab" should be created in your home directory.
<h3>Adding a Kindle for Android Key</h3>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a green plus sign (+). Clicking this button will open a new dialog with two main controls.
<ul>
<li><span class="bold">Choose backup file:</span> click this button and you will be prompted to find the backup.ab file you created earlier. Once selected the file will be processed to extract the decryption key, and if successful the file name will be displayed to the right of the button.</li>
<li><span class="bold">Unique Key Name:</span> this is a unique name you choose to help you identify the key. This name will show in the list of Kindle for Android keys. Enter a name that will help you remember which device this key came from.</li>
</ul>
<p>Click the OK button to store the Kindle for Android key for the current list of Kindle for Android keys. Or click Cancel if you dont want to store the key.</p>
<p>New keys are checked against the current list of keys before being added, and duplicates are discarded.</p>
<h3>Deleting Keys:</h3>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a red "X". Clicking this button will delete the highlighted key in the list. You will be prompted once to be sure thats what you truly mean to do. Once gone, its permanently gone.</p>
<h3>Renaming Keys:</h3>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a sheet of paper. Clicking this button will prompt you to enter a new name for the highlighted key in the list. Enter the new name for the key and click the OK button to use the new name, or Cancel to revert to the old name.</p>
<h3>Exporting Keys:</h3>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a computers hard-drive. Use this button to export the highlighted key to a file (with a .k4a' file name extension). Used for backup purposes or to migrate key data to other computers/calibre installations. The dialog will prompt you for a place to save the file.</p>
<h3>Importing Existing Keyfiles:</h3>
<p>At the bottom-left of the plugins customization dialog, you will see a button labeled "Import Existing Keyfiles". Use this button to import any .k4a file you obtained by using the androidkindlekey.py script manually, or by exporting from another copy of calibre.</p>
</body>
</html>

View File

@@ -1,52 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Managing Kindle for Android serial numbers</title>
<style type="text/css">
span.version {font-size: 50%}
span.bold {font-weight: bold}
h3 {margin-bottom: 0}
p {margin-top: 0}
li {margin-top: 0.5em}
</style>
</head>
<body>
<h1>Managing Kindle for Android serial numbers</h1>
<p>Amazon's Kindle for Android application uses an internal serial number that's 72 character long. Extracting that serial number is a little tricky, but worth it, as it then allows the DRM to be removed from any Kindle ebooks that have been downloaded to that Android device.</p>
<p>Please note that it is not currently known whether the same applies to the Kindle application on the Kindle Fire and Fire HD.</p>
<h3>Getting the Kindle for Android backup file</h3>
<p>Obtain and install adb (Android Debug Bridge) on your computer. Details of how to do this are beyond the scope of this help file, but there are plenty of on-line guides.</p>
<p>Enable developer mode on your Android device. Again, look for an on-line guide for your device.</p>
<p>Once you have adb installed and your device in developer mode, connect your device to your computer with a USB cable and then open up a command line (Terminal on Mac OS X and cmd.exe on Windows) and enter "adb backup com.amazon.kindle" (without the quotation marks!) and press return. A file "backup.ab" should be created in your home directory.
<h3>Adding the Kindle for Android serial number</h3>
<p>At the bottom-left of the plugins customization dialog, you will see a button labeled "Import Existing Keyfiles". Use this button to import the backup.ab file you obtained by using the adb command. The backup file will be processed to extract any serial numbers in it, and the numbers will be added to the list.</p>
<h3>Adding the Kindle for Android serial number manually</h3>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a green plus sign (+). Clicking this button will open a new dialog for entering a new Kindle for Android serial number.</p>
<ul>
<li><span class="bold">Kindle for Android Serial Number:</span> this is the unique serial number of your device. You may have obtained this through using the old android.py script.</li>
</ul>
<p>Click the OK button to save the serial number. Or Cancel if you didnt want to enter a serial number.</p>
<h3>Deleting Kindle for Android serial numbers:</h3>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a red "X". Clicking this button will delete the highlighted Kindle serial number from the list. You will be prompted once to be sure thats what you truly mean to do. Once gone, its permanently gone.</p>
<p>Once done creating/deleting serial numbers, click Close to exit the customization dialogue. Your changes wil only be saved permanently when you click OK in the main configuration dialog.</p>
</body>
</html>

View File

@@ -43,6 +43,9 @@ __docformat__ = 'restructuredtext en'
# 6.2.2 - Added URL method for B&N/nook books
# 6.3.0 - Added in Kindle for Android serial number solution
# 6.3.1 - Version number bump for clarity
# 6.3.2 - Fixed Kindle for Android help file
# 6.3.3 - Bug fix for Kindle for PC support
# 6.3.4 - Fixes for Kindle for Android, Linux, and Kobo 3.17
"""
@@ -50,7 +53,7 @@ Decrypt DRMed ebooks.
"""
PLUGIN_NAME = u"DeDRM"
PLUGIN_VERSION_TUPLE = (6, 3, 1)
PLUGIN_VERSION_TUPLE = (6, 3, 4)
PLUGIN_VERSION = u".".join([unicode(str(x)) for x in PLUGIN_VERSION_TUPLE])
# Include an html helpfile in the plugin's zipfile with the following name.
RESOURCE_NAME = PLUGIN_NAME + '_Help.htm'
@@ -146,7 +149,7 @@ class DeDRM(FileTypePlugin):
try:
open(file_path,'wb').write(data)
except:
print u"{0} v{1}: Exception when copying needed library files after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
print u"{0} v{1}: Exception when copying needed library files".format(PLUGIN_NAME, PLUGIN_VERSION)
traceback.print_exc()
pass
@@ -522,7 +525,7 @@ class DeDRM(FileTypePlugin):
if len(newkeys) > 0:
print u"{0} v{1}: Found {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), u"key" if len(newkeys)==1 else u"keys")
try:
book = k4mobidedrm.GetDecryptedBook(path_to_ebook,newkeys.items(),[],[],self.starttime)
book = k4mobidedrm.GetDecryptedBook(path_to_ebook,newkeys.items(),[],[],[],self.starttime)
decoded = True
# store the new successful keys in the defaults
print u"{0} v{1}: Saving {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), u"key" if len(newkeys)==1 else u"keys")

View File

@@ -16,16 +16,18 @@ from __future__ import with_statement
# - and added in unicode command line support
# 1.3 - added in TkInter interface, output to a file
# 1.4 - Fix some problems identified by Aldo Bleeker
# 1.5 - Fix another problem identified by Aldo Bleeker
"""
Retrieve Kindle for Android Serial Number.
"""
__license__ = 'GPL v3'
__version__ = '1.4'
__version__ = '1.5'
import os
import sys
import traceback
import getopt
import tempfile
import zlib
@@ -220,20 +222,30 @@ def get_serials2(path=STORAGE2):
userdata_keys = cursor.fetchall()
dsns = []
for userdata_row in userdata_keys:
if userdata_row:
userdata_utf8 = userdata_row[0].encode('utf8')
if len(userdata_utf8) > 0:
dsns.append(userdata_utf8)
try:
if userdata_row and userdata_row[0]:
userdata_utf8 = userdata_row[0].encode('utf8')
if len(userdata_utf8) > 0:
dsns.append(userdata_utf8)
except:
print "Error getting one of the device serial name keys"
traceback.print_exc()
pass
dsns = list(set(dsns))
cursor.execute('''select userdata_value from userdata where userdata_key like '%/%kindle.account.tokens%' ''')
userdata_keys = cursor.fetchall()
tokens = []
for userdata_row in userdata_keys:
if userdata_row:
userdata_utf8 = userdata_row[0].encode('utf8')
if len(userdata_utf8) > 0:
tokens.append(userdata_utf8)
try:
if userdata_row and userdata_row[0]:
userdata_utf8 = userdata_row[0].encode('utf8')
if len(userdata_utf8) > 0:
tokens.append(userdata_utf8)
except:
print "Error getting one of the account token keys"
traceback.print_exc()
pass
tokens = list(set(tokens))
serials = []
@@ -377,7 +389,6 @@ def gui_main():
import Tkconstants
import tkMessageBox
import tkFileDialog
import traceback
except:
print "Tkinter not installed"
return cli_main()

View File

@@ -124,7 +124,7 @@ class ConfigWidget(QWidget):
d.exec_()
def kindle_android(self):
d = ManageKeysDialog(self,u"Kindle for Android Keys File",self.tempdedrmprefs['androidkeys'], AddAndroidDialog, 'k4a')
d = ManageKeysDialog(self,u"Kindle for Android Key",self.tempdedrmprefs['androidkeys'], AddAndroidDialog, 'k4a')
d.exec_()
def kindle_keys(self):

View File

@@ -16,8 +16,11 @@
# 6.2.2 - Added URL method for B&N/nook books
# 6.3.0 - Add in Android support
# 6.3.1 - Version bump for clarity
# 6.3.2 - Version bump to match plugin
# 6.3.3 - Version bump to match plugin
# 6.3.4 - Version bump to match plugin
__version__ = '6.3.1'
__version__ = '6.3.4'
import sys
import os, os.path

View File

@@ -0,0 +1,61 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Managing Kindle for Android Keys</title>
<style type="text/css">
span.version {font-size: 50%}
span.bold {font-weight: bold}
h3 {margin-bottom: 0}
p {margin-top: 0}
li {margin-top: 0.5em}
</style>
</head>
<body>
<h1>Managing Kindle for Android Keys</h1>
<p>Amazon's Kindle for Android application uses an internal key equivalent to an eInk Kindle's serial number. Extracting that key is a little tricky, but worth it, as it then allows the DRM to be removed from any Kindle ebooks that have been downloaded to that Android device.</p>
<p>Please note that it is not currently known whether the same applies to the Kindle application on the Kindle Fire and Fire HD.</p>
<h3>Getting the Kindle for Android backup file</h3>
<p>Obtain and install adb (Android Debug Bridge) on your computer. Details of how to do this are beyond the scope of this help file, but there are plenty of on-line guides.</p>
<p>Enable developer mode on your Android device. Again, look for an on-line guide for your device.</p>
<p>Once you have adb installed and your device in developer mode, connect your device to your computer with a USB cable and then open up a command line (Terminal on Mac OS X and cmd.exe on Windows) and enter "adb backup com.amazon.kindle" (without the quotation marks!) and press return. A file "backup.ab" should be created in your home directory.
<h3>Adding a Kindle for Android Key</h3>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a green plus sign (+). Clicking this button will open a new dialog with two main controls.
<ul>
<li><span class="bold">Choose backup file:</span> click this button and you will be prompted to find the backup.ab file you created earlier. Once selected the file will be processed to extract the decryption key, and if successful the file name will be displayed to the right of the button.</li>
<li><span class="bold">Unique Key Name:</span> this is a unique name you choose to help you identify the key. This name will show in the list of Kindle for Android keys. Enter a name that will help you remember which device this key came from.</li>
</ul>
<p>Click the OK button to store the Kindle for Android key for the current list of Kindle for Android keys. Or click Cancel if you dont want to store the key.</p>
<p>New keys are checked against the current list of keys before being added, and duplicates are discarded.</p>
<h3>Deleting Keys:</h3>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a red "X". Clicking this button will delete the highlighted key in the list. You will be prompted once to be sure thats what you truly mean to do. Once gone, its permanently gone.</p>
<h3>Renaming Keys:</h3>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a sheet of paper. Clicking this button will prompt you to enter a new name for the highlighted key in the list. Enter the new name for the key and click the OK button to use the new name, or Cancel to revert to the old name.</p>
<h3>Exporting Keys:</h3>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a computers hard-drive. Use this button to export the highlighted key to a file (with a .k4a' file name extension). Used for backup purposes or to migrate key data to other computers/calibre installations. The dialog will prompt you for a place to save the file.</p>
<h3>Importing Existing Keyfiles:</h3>
<p>At the bottom-left of the plugins customization dialog, you will see a button labeled "Import Existing Keyfiles". Use this button to import any .k4a file you obtained by using the androidkindlekey.py script manually, or by exporting from another copy of calibre.</p>
</body>
</html>

View File

@@ -1,52 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Managing Kindle for Android serial numbers</title>
<style type="text/css">
span.version {font-size: 50%}
span.bold {font-weight: bold}
h3 {margin-bottom: 0}
p {margin-top: 0}
li {margin-top: 0.5em}
</style>
</head>
<body>
<h1>Managing Kindle for Android serial numbers</h1>
<p>Amazon's Kindle for Android application uses an internal serial number that's 72 character long. Extracting that serial number is a little tricky, but worth it, as it then allows the DRM to be removed from any Kindle ebooks that have been downloaded to that Android device.</p>
<p>Please note that it is not currently known whether the same applies to the Kindle application on the Kindle Fire and Fire HD.</p>
<h3>Getting the Kindle for Android backup file</h3>
<p>Obtain and install adb (Android Debug Bridge) on your computer. Details of how to do this are beyond the scope of this help file, but there are plenty of on-line guides.</p>
<p>Enable developer mode on your Android device. Again, look for an on-line guide for your device.</p>
<p>Once you have adb installed and your device in developer mode, connect your device to your computer with a USB cable and then open up a command line (Terminal on Mac OS X and cmd.exe on Windows) and enter "adb backup com.amazon.kindle" (without the quotation marks!) and press return. A file "backup.ab" should be created in your home directory.
<h3>Adding the Kindle for Android serial number</h3>
<p>At the bottom-left of the plugins customization dialog, you will see a button labeled "Import Existing Keyfiles". Use this button to import the backup.ab file you obtained by using the adb command. The backup file will be processed to extract any serial numbers in it, and the numbers will be added to the list.</p>
<h3>Adding the Kindle for Android serial number manually</h3>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a green plus sign (+). Clicking this button will open a new dialog for entering a new Kindle for Android serial number.</p>
<ul>
<li><span class="bold">Kindle for Android Serial Number:</span> this is the unique serial number of your device. You may have obtained this through using the old android.py script.</li>
</ul>
<p>Click the OK button to save the serial number. Or Cancel if you didnt want to enter a serial number.</p>
<h3>Deleting Kindle for Android serial numbers:</h3>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a red "X". Clicking this button will delete the highlighted Kindle serial number from the list. You will be prompted once to be sure thats what you truly mean to do. Once gone, its permanently gone.</p>
<p>Once done creating/deleting serial numbers, click Close to exit the customization dialogue. Your changes wil only be saved permanently when you click OK in the main configuration dialog.</p>
</body>
</html>

View File

@@ -43,6 +43,9 @@ __docformat__ = 'restructuredtext en'
# 6.2.2 - Added URL method for B&N/nook books
# 6.3.0 - Added in Kindle for Android serial number solution
# 6.3.1 - Version number bump for clarity
# 6.3.2 - Fixed Kindle for Android help file
# 6.3.3 - Bug fix for Kindle for PC support
# 6.3.4 - Fixes for Kindle for Android, Linux, and Kobo 3.17
"""
@@ -50,7 +53,7 @@ Decrypt DRMed ebooks.
"""
PLUGIN_NAME = u"DeDRM"
PLUGIN_VERSION_TUPLE = (6, 3, 1)
PLUGIN_VERSION_TUPLE = (6, 3, 4)
PLUGIN_VERSION = u".".join([unicode(str(x)) for x in PLUGIN_VERSION_TUPLE])
# Include an html helpfile in the plugin's zipfile with the following name.
RESOURCE_NAME = PLUGIN_NAME + '_Help.htm'
@@ -146,7 +149,7 @@ class DeDRM(FileTypePlugin):
try:
open(file_path,'wb').write(data)
except:
print u"{0} v{1}: Exception when copying needed library files after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
print u"{0} v{1}: Exception when copying needed library files".format(PLUGIN_NAME, PLUGIN_VERSION)
traceback.print_exc()
pass
@@ -522,7 +525,7 @@ class DeDRM(FileTypePlugin):
if len(newkeys) > 0:
print u"{0} v{1}: Found {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), u"key" if len(newkeys)==1 else u"keys")
try:
book = k4mobidedrm.GetDecryptedBook(path_to_ebook,newkeys.items(),[],[],self.starttime)
book = k4mobidedrm.GetDecryptedBook(path_to_ebook,newkeys.items(),[],[],[],self.starttime)
decoded = True
# store the new successful keys in the defaults
print u"{0} v{1}: Saving {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), u"key" if len(newkeys)==1 else u"keys")

View File

@@ -16,16 +16,18 @@ from __future__ import with_statement
# - and added in unicode command line support
# 1.3 - added in TkInter interface, output to a file
# 1.4 - Fix some problems identified by Aldo Bleeker
# 1.5 - Fix another problem identified by Aldo Bleeker
"""
Retrieve Kindle for Android Serial Number.
"""
__license__ = 'GPL v3'
__version__ = '1.4'
__version__ = '1.5'
import os
import sys
import traceback
import getopt
import tempfile
import zlib
@@ -220,20 +222,30 @@ def get_serials2(path=STORAGE2):
userdata_keys = cursor.fetchall()
dsns = []
for userdata_row in userdata_keys:
if userdata_row:
userdata_utf8 = userdata_row[0].encode('utf8')
if len(userdata_utf8) > 0:
dsns.append(userdata_utf8)
try:
if userdata_row and userdata_row[0]:
userdata_utf8 = userdata_row[0].encode('utf8')
if len(userdata_utf8) > 0:
dsns.append(userdata_utf8)
except:
print "Error getting one of the device serial name keys"
traceback.print_exc()
pass
dsns = list(set(dsns))
cursor.execute('''select userdata_value from userdata where userdata_key like '%/%kindle.account.tokens%' ''')
userdata_keys = cursor.fetchall()
tokens = []
for userdata_row in userdata_keys:
if userdata_row:
userdata_utf8 = userdata_row[0].encode('utf8')
if len(userdata_utf8) > 0:
tokens.append(userdata_utf8)
try:
if userdata_row and userdata_row[0]:
userdata_utf8 = userdata_row[0].encode('utf8')
if len(userdata_utf8) > 0:
tokens.append(userdata_utf8)
except:
print "Error getting one of the account token keys"
traceback.print_exc()
pass
tokens = list(set(tokens))
serials = []
@@ -377,7 +389,6 @@ def gui_main():
import Tkconstants
import tkMessageBox
import tkFileDialog
import traceback
except:
print "Tkinter not installed"
return cli_main()

View File

@@ -124,7 +124,7 @@ class ConfigWidget(QWidget):
d.exec_()
def kindle_android(self):
d = ManageKeysDialog(self,u"Kindle for Android Keys File",self.tempdedrmprefs['androidkeys'], AddAndroidDialog, 'k4a')
d = ManageKeysDialog(self,u"Kindle for Android Key",self.tempdedrmprefs['androidkeys'], AddAndroidDialog, 'k4a')
d.exec_()
def kindle_keys(self):

View File

@@ -5,9 +5,10 @@ DeDRM_App.pyw is a python drag and drop application that allows users to drag an
It will work without manual configuration for Kindle for PC ebooks, Adobe Digital Edition epub and pdf ebooks and Barnes & Noble NOOK Study ePubs when Kindle for PC, Adobe Digital Editions and NOOK Study are installed on the same computer and user account.
To remove the DRM from eInk Kindle ebooks, Mobipocket ebooks and Fictionwise eReader ebooks requires the user to double-click the DeDRM_Drop_Target.bat file and set some additional Preferences including:
To remove the DRM from eInk Kindle ebooks, Kindle for Android ebooks, Mobipocket ebooks and Fictionwise eReader ebooks requires the user to double-click the DeDRM_Drop_Target.bat file and set some additional Preferences including:
eInk Kindle: 16 digit Serial Number
Kindle for Android: backup.ab file, details below.
eReader Social DRM: Name:Last 8 digits of CC number
MobiPocket: 10 digit PID
@@ -27,6 +28,17 @@ Installation
3. To set the preferences simply double-click on the short-cut you've just created.
Kindle for Android
------------------
The backup.ab file needs to be obtained using Android-specific tools.
Obtain and install adb (Android Debug Bridge) on your computer. Details of how to do this are beyond the scope of this readme file, but there are plenty of on-line guides.
Enable developer mode on your Android device. Again, look for an on-line guide for your device.
Once you have adb installed and your device in developer mode, connect your device to your computer with a USB cable and then open up a command line (Terminal on Mac OS X and cmd.exe on Windows) and enter "adb backup com.amazon.kindle" (without the quotation marks!) and press return. A file "backup.ab" should be created in your home directory.
Credits
-------
The original inept and ignoble scripts were by i♥cabbages

View File

@@ -0,0 +1,61 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Managing Kindle for Android Keys</title>
<style type="text/css">
span.version {font-size: 50%}
span.bold {font-weight: bold}
h3 {margin-bottom: 0}
p {margin-top: 0}
li {margin-top: 0.5em}
</style>
</head>
<body>
<h1>Managing Kindle for Android Keys</h1>
<p>Amazon's Kindle for Android application uses an internal key equivalent to an eInk Kindle's serial number. Extracting that key is a little tricky, but worth it, as it then allows the DRM to be removed from any Kindle ebooks that have been downloaded to that Android device.</p>
<p>Please note that it is not currently known whether the same applies to the Kindle application on the Kindle Fire and Fire HD.</p>
<h3>Getting the Kindle for Android backup file</h3>
<p>Obtain and install adb (Android Debug Bridge) on your computer. Details of how to do this are beyond the scope of this help file, but there are plenty of on-line guides.</p>
<p>Enable developer mode on your Android device. Again, look for an on-line guide for your device.</p>
<p>Once you have adb installed and your device in developer mode, connect your device to your computer with a USB cable and then open up a command line (Terminal on Mac OS X and cmd.exe on Windows) and enter "adb backup com.amazon.kindle" (without the quotation marks!) and press return. A file "backup.ab" should be created in your home directory.
<h3>Adding a Kindle for Android Key</h3>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a green plus sign (+). Clicking this button will open a new dialog with two main controls.
<ul>
<li><span class="bold">Choose backup file:</span> click this button and you will be prompted to find the backup.ab file you created earlier. Once selected the file will be processed to extract the decryption key, and if successful the file name will be displayed to the right of the button.</li>
<li><span class="bold">Unique Key Name:</span> this is a unique name you choose to help you identify the key. This name will show in the list of Kindle for Android keys. Enter a name that will help you remember which device this key came from.</li>
</ul>
<p>Click the OK button to store the Kindle for Android key for the current list of Kindle for Android keys. Or click Cancel if you dont want to store the key.</p>
<p>New keys are checked against the current list of keys before being added, and duplicates are discarded.</p>
<h3>Deleting Keys:</h3>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a red "X". Clicking this button will delete the highlighted key in the list. You will be prompted once to be sure thats what you truly mean to do. Once gone, its permanently gone.</p>
<h3>Renaming Keys:</h3>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a sheet of paper. Clicking this button will prompt you to enter a new name for the highlighted key in the list. Enter the new name for the key and click the OK button to use the new name, or Cancel to revert to the old name.</p>
<h3>Exporting Keys:</h3>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a computers hard-drive. Use this button to export the highlighted key to a file (with a .k4a' file name extension). Used for backup purposes or to migrate key data to other computers/calibre installations. The dialog will prompt you for a place to save the file.</p>
<h3>Importing Existing Keyfiles:</h3>
<p>At the bottom-left of the plugins customization dialog, you will see a button labeled "Import Existing Keyfiles". Use this button to import any .k4a file you obtained by using the androidkindlekey.py script manually, or by exporting from another copy of calibre.</p>
</body>
</html>

View File

@@ -1,52 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Managing Kindle for Android serial numbers</title>
<style type="text/css">
span.version {font-size: 50%}
span.bold {font-weight: bold}
h3 {margin-bottom: 0}
p {margin-top: 0}
li {margin-top: 0.5em}
</style>
</head>
<body>
<h1>Managing Kindle for Android serial numbers</h1>
<p>Amazon's Kindle for Android application uses an internal serial number that's 72 character long. Extracting that serial number is a little tricky, but worth it, as it then allows the DRM to be removed from any Kindle ebooks that have been downloaded to that Android device.</p>
<p>Please note that it is not currently known whether the same applies to the Kindle application on the Kindle Fire and Fire HD.</p>
<h3>Getting the Kindle for Android backup file</h3>
<p>Obtain and install adb (Android Debug Bridge) on your computer. Details of how to do this are beyond the scope of this help file, but there are plenty of on-line guides.</p>
<p>Enable developer mode on your Android device. Again, look for an on-line guide for your device.</p>
<p>Once you have adb installed and your device in developer mode, connect your device to your computer with a USB cable and then open up a command line (Terminal on Mac OS X and cmd.exe on Windows) and enter "adb backup com.amazon.kindle" (without the quotation marks!) and press return. A file "backup.ab" should be created in your home directory.
<h3>Adding the Kindle for Android serial number</h3>
<p>At the bottom-left of the plugins customization dialog, you will see a button labeled "Import Existing Keyfiles". Use this button to import the backup.ab file you obtained by using the adb command. The backup file will be processed to extract any serial numbers in it, and the numbers will be added to the list.</p>
<h3>Adding the Kindle for Android serial number manually</h3>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a green plus sign (+). Clicking this button will open a new dialog for entering a new Kindle for Android serial number.</p>
<ul>
<li><span class="bold">Kindle for Android Serial Number:</span> this is the unique serial number of your device. You may have obtained this through using the old android.py script.</li>
</ul>
<p>Click the OK button to save the serial number. Or Cancel if you didnt want to enter a serial number.</p>
<h3>Deleting Kindle for Android serial numbers:</h3>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a red "X". Clicking this button will delete the highlighted Kindle serial number from the list. You will be prompted once to be sure thats what you truly mean to do. Once gone, its permanently gone.</p>
<p>Once done creating/deleting serial numbers, click Close to exit the customization dialogue. Your changes wil only be saved permanently when you click OK in the main configuration dialog.</p>
</body>
</html>

View File

@@ -43,6 +43,9 @@ __docformat__ = 'restructuredtext en'
# 6.2.2 - Added URL method for B&N/nook books
# 6.3.0 - Added in Kindle for Android serial number solution
# 6.3.1 - Version number bump for clarity
# 6.3.2 - Fixed Kindle for Android help file
# 6.3.3 - Bug fix for Kindle for PC support
# 6.3.4 - Fixes for Kindle for Android, Linux, and Kobo 3.17
"""
@@ -50,7 +53,7 @@ Decrypt DRMed ebooks.
"""
PLUGIN_NAME = u"DeDRM"
PLUGIN_VERSION_TUPLE = (6, 3, 1)
PLUGIN_VERSION_TUPLE = (6, 3, 4)
PLUGIN_VERSION = u".".join([unicode(str(x)) for x in PLUGIN_VERSION_TUPLE])
# Include an html helpfile in the plugin's zipfile with the following name.
RESOURCE_NAME = PLUGIN_NAME + '_Help.htm'
@@ -146,7 +149,7 @@ class DeDRM(FileTypePlugin):
try:
open(file_path,'wb').write(data)
except:
print u"{0} v{1}: Exception when copying needed library files after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime)
print u"{0} v{1}: Exception when copying needed library files".format(PLUGIN_NAME, PLUGIN_VERSION)
traceback.print_exc()
pass
@@ -522,7 +525,7 @@ class DeDRM(FileTypePlugin):
if len(newkeys) > 0:
print u"{0} v{1}: Found {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), u"key" if len(newkeys)==1 else u"keys")
try:
book = k4mobidedrm.GetDecryptedBook(path_to_ebook,newkeys.items(),[],[],self.starttime)
book = k4mobidedrm.GetDecryptedBook(path_to_ebook,newkeys.items(),[],[],[],self.starttime)
decoded = True
# store the new successful keys in the defaults
print u"{0} v{1}: Saving {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), u"key" if len(newkeys)==1 else u"keys")

View File

@@ -16,16 +16,18 @@ from __future__ import with_statement
# - and added in unicode command line support
# 1.3 - added in TkInter interface, output to a file
# 1.4 - Fix some problems identified by Aldo Bleeker
# 1.5 - Fix another problem identified by Aldo Bleeker
"""
Retrieve Kindle for Android Serial Number.
"""
__license__ = 'GPL v3'
__version__ = '1.4'
__version__ = '1.5'
import os
import sys
import traceback
import getopt
import tempfile
import zlib
@@ -220,20 +222,30 @@ def get_serials2(path=STORAGE2):
userdata_keys = cursor.fetchall()
dsns = []
for userdata_row in userdata_keys:
if userdata_row:
userdata_utf8 = userdata_row[0].encode('utf8')
if len(userdata_utf8) > 0:
dsns.append(userdata_utf8)
try:
if userdata_row and userdata_row[0]:
userdata_utf8 = userdata_row[0].encode('utf8')
if len(userdata_utf8) > 0:
dsns.append(userdata_utf8)
except:
print "Error getting one of the device serial name keys"
traceback.print_exc()
pass
dsns = list(set(dsns))
cursor.execute('''select userdata_value from userdata where userdata_key like '%/%kindle.account.tokens%' ''')
userdata_keys = cursor.fetchall()
tokens = []
for userdata_row in userdata_keys:
if userdata_row:
userdata_utf8 = userdata_row[0].encode('utf8')
if len(userdata_utf8) > 0:
tokens.append(userdata_utf8)
try:
if userdata_row and userdata_row[0]:
userdata_utf8 = userdata_row[0].encode('utf8')
if len(userdata_utf8) > 0:
tokens.append(userdata_utf8)
except:
print "Error getting one of the account token keys"
traceback.print_exc()
pass
tokens = list(set(tokens))
serials = []
@@ -377,7 +389,6 @@ def gui_main():
import Tkconstants
import tkMessageBox
import tkFileDialog
import traceback
except:
print "Tkinter not installed"
return cli_main()

View File

@@ -124,7 +124,7 @@ class ConfigWidget(QWidget):
d.exec_()
def kindle_android(self):
d = ManageKeysDialog(self,u"Kindle for Android Keys File",self.tempdedrmprefs['androidkeys'], AddAndroidDialog, 'k4a')
d = ManageKeysDialog(self,u"Kindle for Android Key",self.tempdedrmprefs['androidkeys'], AddAndroidDialog, 'k4a')
d.exec_()
def kindle_keys(self):

View File

@@ -26,6 +26,7 @@ def WineGetKeys(scriptpath, extension, wineprefix=""):
if not os.path.exists(outdirpath):
os.makedirs(outdirpath)
wineprefix = os.path.abspath(os.path.expanduser(os.path.expandvars(wineprefix)))
if wineprefix != "" and os.path.exists(wineprefix):
cmdline = u"WINEPREFIX=\"{2}\" wine python.exe \"{0}\" \"{1}\"".format(scriptpath,outdirpath,wineprefix)
else:

Binary file not shown.

View File

@@ -19,7 +19,7 @@ except NameError:
PLUGIN_NAME = 'Obok DeDRM'
PLUGIN_SAFE_NAME = PLUGIN_NAME.strip().lower().replace(' ', '_')
PLUGIN_DESCRIPTION = _('Removes DRM from Kobo kepubs and adds them to the library.')
PLUGIN_VERSION_TUPLE = (3, 1, 2)
PLUGIN_VERSION_TUPLE = (3, 1, 6)
PLUGIN_VERSION = '.'.join([str(x) for x in PLUGIN_VERSION_TUPLE])
HELPFILE_NAME = PLUGIN_SAFE_NAME + '_Help.htm'
PLUGIN_AUTHORS = 'Anon'
@@ -29,7 +29,7 @@ class ObokDeDRMAction(InterfaceActionBase):
name = PLUGIN_NAME
description = PLUGIN_DESCRIPTION
supported_platforms = ['windows', 'osx']
supported_platforms = ['windows', 'osx', 'linux' ]
author = PLUGIN_AUTHORS
version = PLUGIN_VERSION_TUPLE
minimum_calibre_version = (1, 0, 0)

View File

@@ -6,7 +6,7 @@ __license__ = 'GPL v3'
__docformat__ = 'restructuredtext en'
import os, zipfile
import os, traceback, zipfile
try:
from PyQt5.Qt import QToolButton, QUrl
@@ -34,6 +34,15 @@ from calibre_plugins.obok_dedrm.utilities import (
from calibre_plugins.obok_dedrm.obok.obok import KoboLibrary
from calibre_plugins.obok_dedrm.obok.legacy_obok import legacy_obok
can_parse_xml = True
try:
from xml.etree import ElementTree as ET
debug_print("using xml.etree for xml parsing")
except ImportError:
can_parse_xml = False
debug_print("Cannot find xml.etree, disabling extraction of serial numbers")
PLUGIN_ICONS = ['images/obok.png']
try:
@@ -78,8 +87,42 @@ class InterfacePluginAction(InterfaceAction):
self.current_idx = self.gui.library_view.currentIndex()
print ('Running {}'.format(PLUGIN_NAME + ' v' + PLUGIN_VERSION))
#
# search for connected device in case serials are saved
tmpserials = cfg['kobo_serials']
device = self.parent().device_manager.connected_device
device_path = None
if (device):
device_path = device._main_prefix
debug_print("get_device_settings - device_path=", device_path)
# get serial from device_path/.adobe-digital-editions/device.xml
if can_parse_xml:
devicexml = os.path.join(device_path, '.adobe-digital-editions', 'device.xml')
debug_print("trying to load %s" % devicexml)
if (os.path.exists(devicexml)):
debug_print("trying to parse %s" % devicexml)
xmltree = ET.parse(devicexml)
for node in xmltree.iter():
if "deviceSerial" in node.tag:
serial = node.text
debug_print ("found serial %s" % serial)
tmpserials.append(serial)
break
else:
debug_print("didn't find device")
# Get the Kobo Library object (obok v3.01)
self.library = KoboLibrary()
self.library = KoboLibrary(tmpserials, device_path)
debug_print ("got kobodir %s" % self.library.kobodir)
if (self.library.kobodir == ''):
# linux and no device connected, but could be extended
# to the case where on Windows/Mac the prog is not installed
msg = _('<p>Could not find Kobo Library\n<p>Windows/Mac: do you have Kobo Desktop installed?\n<p>Windows/Mac/Linux: In case you have an Kobo eInk device, connect the device.')
showErrorDlg(msg, None)
return
# Get a list of Kobo titles
books = self.build_book_list()
@@ -98,6 +141,7 @@ class InterfacePluginAction(InterfaceAction):
candidate_keys = self.library.userkeys
except:
print (_('Trouble retrieving keys with newer obok method.'))
traceback.print_exc()
else:
if len(candidate_keys):
self.userkeys.extend(candidate_keys)

View File

@@ -3,15 +3,23 @@ from __future__ import (unicode_literals, division, absolute_import,
print_function)
try:
from PyQt5.Qt import (QWidget, QLabel, QVBoxLayout, QHBoxLayout, QComboBox)
from PyQt5.Qt import (Qt, QGroupBox, QListWidget, QLineEdit, QDialogButtonBox, QWidget, QLabel, QDialog, QVBoxLayout, QAbstractItemView, QIcon, QHBoxLayout, QComboBox, QListWidgetItem)
except ImportError:
from PyQt4.Qt import (QWidget, QLabel, QVBoxLayout, QHBoxLayout, QComboBox)
from PyQt4.Qt import (Qt, QGroupBox, QListWidget, QLineEdit, QDialogButtonBox, QWidget, QLabel, QDialog, QVBoxLayout, QAbstractItemView, QIcon, QHBoxLayout, QComboBox, QListWidgetItem)
try:
from PyQt5 import Qt as QtGui
except ImportError:
from PyQt4 import QtGui
from calibre.gui2 import (error_dialog, question_dialog, info_dialog, open_url)
from calibre.utils.config import JSONConfig, config_dir
plugin_prefs = JSONConfig('plugins/obok_dedrm_prefs')
plugin_prefs.defaults['finding_homes_for_formats'] = 'Ask'
plugin_prefs.defaults['kobo_serials'] = []
from calibre_plugins.obok_dedrm.__init__ import PLUGIN_NAME, PLUGIN_VERSION
from calibre_plugins.obok_dedrm.utilities import (debug_print)
try:
debug_print("obok::config.py - loading translations")
@@ -26,7 +34,10 @@ class ConfigWidget(QWidget):
self.plugin_action = plugin_action
layout = QVBoxLayout(self)
self.setLayout(layout)
# copy of preferences
self.tmpserials = plugin_prefs['kobo_serials']
combo_label = QLabel(_('When should Obok try to insert EPUBs into existing calibre entries?'), self)
layout.addWidget(combo_label)
self.find_homes = QComboBox()
@@ -35,6 +46,178 @@ class ConfigWidget(QWidget):
self.find_homes.addItems([_('Ask'), _('Always'), _('Never')])
index = self.find_homes.findText(plugin_prefs['finding_homes_for_formats'])
self.find_homes.setCurrentIndex(index)
self.serials_button = QtGui.QPushButton(self)
self.serials_button.setToolTip(_(u"Click to manage Kobo serial numbers for Kobo ebooks"))
self.serials_button.setText(u"Kobo devices serials")
self.serials_button.clicked.connect(self.edit_serials)
layout.addWidget(self.serials_button)
def edit_serials(self):
d = ManageKeysDialog(self,u"Kobo device serial numbers",self.tmpserials, AddSerialDialog)
d.exec_()
def save_settings(self):
plugin_prefs['finding_homes_for_formats'] = unicode(self.find_homes.currentText())
plugin_prefs['kobo_serials'] = self.tmpserials
class ManageKeysDialog(QDialog):
def __init__(self, parent, key_type_name, plugin_keys, create_key, keyfile_ext = u""):
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
self.json_file = (keyfile_ext == u"k4i")
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)
keys_group_box = QGroupBox(_(u"{0}s".format(self.key_type_name)), self)
layout.addWidget(keys_group_box)
keys_group_box_layout = QHBoxLayout()
keys_group_box.setLayout(keys_group_box_layout)
self.listy = QListWidget(self)
self.listy.setToolTip(u"{0}s that will be used to decrypt ebooks".format(self.key_type_name))
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')))
self._add_key_button.setToolTip(u"Create new {0}".format(self.key_type_name))
self._add_key_button.clicked.connect(self.add_key)
button_layout.addWidget(self._add_key_button)
self._delete_key_button = QtGui.QToolButton(self)
self._delete_key_button.setToolTip(_(u"Delete highlighted key"))
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)
spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
button_layout.addItem(spacerItem)
layout.addSpacing(5)
migrate_layout = QHBoxLayout()
layout.addLayout(migrate_layout)
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 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))
def add_key(self):
d = self.create_key(self)
d.exec_()
if d.result() != d.Accepted:
# New key generation cancelled.
return
new_key_value = d.key_value
if new_key_value in self.plugin_keys:
info_dialog(None, "{0} {1}: Duplicate {2}".format(PLUGIN_NAME, PLUGIN_VERSION,self.key_type_name),
u"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)
self.listy.clear()
self.populate_list()
def rename_key(self):
if not self.listy.currentItem():
errmsg = u"No {0} selected to rename. Highlight a keyfile first.".format(self.key_type_name)
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
keyname = unicode(self.listy.currentItem().text())
if not question_dialog(self, "{0} {1}: Confirm Rename".format(PLUGIN_NAME, PLUGIN_VERSION), u"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):
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
keyname = unicode(self.listy.currentItem().text())
if not question_dialog(self, "{0} {1}: Confirm Delete".format(PLUGIN_NAME, PLUGIN_VERSION), u"Do you really want to delete the {1} <strong>{0}</strong>?".format(keyname, self.key_type_name), show_copy_button=False, default_yes=False):
return
self.plugin_keys.remove(keyname)
self.listy.clear()
self.populate_list()
class AddSerialDialog(QDialog):
def __init__(self, parent=None,):
QDialog.__init__(self, parent)
self.parent = parent
self.setWindowTitle(u"{0} {1}: Add New eInk Kobo Serial Number".format(PLUGIN_NAME, PLUGIN_VERSION))
layout = QVBoxLayout(self)
self.setLayout(layout)
data_group_box = QGroupBox(u"", 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(u"EInk Kobo Serial Number:", self))
self.key_ledit = QLineEdit("", self)
self.key_ledit.setToolTip(u"Enter an eInk Kobo serial number. EInk Kobo serial numbers are 13 characters long and usually start with a 'N'. Kobo Serial Numbers are case-sensitive, so be sure to enter the upper and lower case letters unchanged.")
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 unicode(self.key_ledit.text()).strip()
@property
def key_value(self):
return unicode(self.key_ledit.text()).strip()
def accept(self):
if len(self.key_name) == 0 or self.key_name.isspace():
errmsg = u"Please enter an eInk Kindle Serial Number or click Cancel in the dialog."
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
if len(self.key_name) != 13:
errmsg = u"EInk Kobo Serial Numbers must be 13 characters long. This is {0:d} characters long.".format(len(self.key_name))
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
QDialog.accept(self)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -1,6 +1,20 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Version 3.1.6 September 2015
# Enable support for Kobo devices
# More character encoding fixes (unicode strings)
#
# Version 3.1.5 September 2015
# Removed requirement that a purchase has been made.
# Also add in character encoding fixes
#
# Version 3.1.4 September 2015
# Updated for version 3.17 of the Windows Desktop app.
#
# Version 3.1.3 August 2015
# Add translations for Portuguese and Arabic
#
# Version 3.1.2 January 2015
# Add coding, version number and version announcement
#
@@ -109,7 +123,7 @@
#
"""Manage all Kobo books, either encrypted or DRM-free."""
__version__ = '3.1.1'
__version__ = '3.1.6'
import sys
import os
@@ -209,6 +223,24 @@ def _load_crypto():
AES = _load_crypto()
# Wrap a stream so that output gets flushed immediately
# and also make sure that any unicode strings get
# encoded using "replace" before writing them.
class SafeUnbuffered:
def __init__(self, stream):
self.stream = stream
self.encoding = stream.encoding
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
if isinstance(data,unicode):
data = data.encode(self.encoding,"replace")
self.stream.write(data)
self.stream.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
class KoboLibrary(object):
"""The Kobo library.
@@ -216,23 +248,49 @@ class KoboLibrary(object):
written by the Kobo Desktop Edition application, including the list
of books, their titles, and the user's encryption key(s)."""
def __init__ (self):
print u"Obok v{0}\nCopyright © 2012-2014 Physisticated et al.".format(__version__)
if sys.platform.startswith('win'):
if sys.getwindowsversion().major > 5:
self.kobodir = os.environ['LOCALAPPDATA']
else:
self.kobodir = os.path.join(os.environ['USERPROFILE'], 'Local Settings', 'Application Data')
self.kobodir = os.path.join(self.kobodir, 'Kobo', 'Kobo Desktop Edition')
elif sys.platform.startswith('darwin'):
self.kobodir = os.path.join(os.environ['HOME'], 'Library', 'Application Support', 'Kobo', 'Kobo Desktop Edition')
self.bookdir = os.path.join(self.kobodir, 'kepub')
kobodb = os.path.join(self.kobodir, 'Kobo.sqlite')
self.__sqlite = sqlite3.connect(kobodb)
self.__cursor = self.__sqlite.cursor()
self._userkeys = []
self._books = []
self._volumeID = []
def __init__ (self, serials = [], device_path = None):
print u"Obok v{0}\nCopyright © 2012-2015 Physisticated et al.".format(__version__)
self.kobodir = u""
kobodb = u""
# - first check whether serials have been found or are provided
# and a device is connected. In this case, use the device
# - otherwise fall back to Kobo Desktop Application for Windows and Mac
if (device_path and (len(serials) > 0)):
self.kobodir = os.path.join(device_path, u".kobo")
# devices use KoboReader.sqlite
kobodb = os.path.join(self.kobodir, u"KoboReader.sqlite")
if (not(os.path.exists(kobodb))):
# give up here, we haven't found anything useful
self.kobodir = u""
kobodb = u""
if (self.kobodir == u""):
# we haven't found a device with serials, so try desktop apps
if sys.platform.startswith('win'):
import _winreg as winreg
if sys.getwindowsversion().major > 5:
if 'LOCALAPPDATA' in os.environ.keys():
# Python 2.x does not return unicode env. Use Python 3.x
self.kobodir = winreg.ExpandEnvironmentStrings(u"%LOCALAPPDATA%")
if (self.kobodir == u""):
if 'USERPROFILE' in os.environ.keys():
# Python 2.x does not return unicode env. Use Python 3.x
self.kobodir = os.path.join(winreg.ExpandEnvironmentStrings(u"%USERPROFILE%"), u"Local Settings", u"Application Data")
self.kobodir = os.path.join(self.kobodir, u"Kobo", u"Kobo Desktop Edition")
elif sys.platform.startswith('darwin'):
self.kobodir = os.path.join(os.environ['HOME'], u"Library", u"Application Support", u"Kobo", u"Kobo Desktop Edition")
# desktop versions use Kobo.sqlite
kobodb = os.path.join(self.kobodir, u"Kobo.sqlite")
if (self.kobodir != u""):
self.bookdir = os.path.join(self.kobodir, u"kepub")
self.__sqlite = sqlite3.connect(kobodb)
self.__cursor = self.__sqlite.cursor()
self._userkeys = []
self._books = []
self._volumeID = []
self._serials = serials
def close (self):
"""Closes the database used by the library."""
@@ -246,9 +304,8 @@ class KoboLibrary(object):
"""
if len(self._userkeys) != 0:
return self._userkeys
userid = self.__getuserid()
for macaddr in self.__getmacaddrs():
self._userkeys.append(self.__getuserkey(macaddr, userid))
self._userkeys.extend(self.__getuserkeys(macaddr))
return self._userkeys
@property
@@ -274,7 +331,7 @@ class KoboLibrary(object):
def __bookfile (self, volumeid):
"""The filename needed to open a given book."""
return os.path.join(self.kobodir, 'kepub', volumeid)
return os.path.join(self.kobodir, u"kepub", volumeid)
def __getmacaddrs (self):
"""The list of all MAC addresses on this machine."""
@@ -290,17 +347,42 @@ class KoboLibrary(object):
output = subprocess.check_output('/sbin/ifconfig -a', shell=True)
matches = c.findall(output)
for m in matches:
# print "m:",m[0]
# print u"m:{0}".format(m[0])
macaddrs.append(m[0].upper())
# extend the list of macaddrs in any case with the serials
# cannot hurt ;-)
macaddrs.extend(self._serials)
return macaddrs
def __getuserid (self):
return self.__cursor.execute('SELECT UserID FROM user WHERE HasMadePurchase = "true"').fetchone()[0]
def __getuserkey (self, macaddr, userid):
def __getuserids (self):
userids = []
cursor = self.__cursor.execute('SELECT UserID FROM user')
row = cursor.fetchone()
while row is not None:
try:
userid = row[0]
userids.append(userid)
except:
pass
row = cursor.fetchone()
return userids
def __getuserkeys (self, macaddr):
userids = self.__getuserids()
userkeys = []
# This version is used for versions before 3.17.0.
deviceid = hashlib.sha256('NoCanLook' + macaddr).hexdigest()
userkey = hashlib.sha256(deviceid + userid).hexdigest()
return binascii.a2b_hex(userkey[32:])
for userid in userids:
userkey = hashlib.sha256(deviceid + userid).hexdigest()
userkeys.append(binascii.a2b_hex(userkey[32:]))
# This version is used for 3.17.0 and later.
deviceid = hashlib.sha256('XzUhGYdFp' + macaddr).hexdigest()
for userid in userids:
userkey = hashlib.sha256(deviceid + userid).hexdigest()
userkeys.append(binascii.a2b_hex(userkey[32:]))
return userkeys
class KoboBook(object):
"""A Kobo book.
@@ -407,13 +489,13 @@ class KoboFile(object):
if contents[:5]=="<?xml":
return True
else:
print "Bad XML: ",contents[:5]
print u"Bad XML: {0}".format(contents[:5])
raise ValueError
if self.mimetype == 'image/jpeg':
if contents[:3] == '\xff\xd8\xff':
return True
else:
print "Bad JPEG: ", contents[:3].encode('hex')
print u"Bad JPEG: {0}".format(contents[:3].encode('hex'))
raise ValueError()
return False
@@ -435,36 +517,34 @@ class KoboFile(object):
contents = contents[:-padding]
return contents
if __name__ == '__main__':
def cli_main():
lib = KoboLibrary()
for i, book in enumerate(lib.books):
print ('%d: %s' % (i + 1, book.title)).encode('ascii', 'ignore')
print u"{0}: {1}".format(i + 1, book.title)
num_string = raw_input("Convert book number... ")
num_string = raw_input(u"Convert book number... ")
try:
num = int(num_string)
book = lib.books[num - 1]
except (ValueError, IndexError):
exit()
print "Converting", book.title
print u"Converting {0}".format(book.title)
zin = zipfile.ZipFile(book.filename, "r")
# make filename out of Unicode alphanumeric and whitespace equivalents from title
outname = "%s.epub" % (re.sub('[^\s\w]', '', book.title, 0, re.UNICODE))
outname = u"{0}.epub".format(re.sub('[^\s\w]', '_', book.title, 0, re.UNICODE))
if (book.type == 'drm-free'):
print "DRM-free book, conversion is not needed"
print u"DRM-free book, conversion is not needed"
shutil.copyfile(book.filename, outname)
print "Book saved as", os.path.join(os.getcwd(), outname)
print u"Book saved as {0}".format(os.path.join(os.getcwd(), outname))
exit(0)
result = 1
for userkey in lib.userkeys:
# print "Trying key: ",userkey.encode('hex_codec')
confirmedGood = False
print u"Trying key: {0}".format(userkey.encode('hex_codec'))
try:
zout = zipfile.ZipFile(outname, "w", zipfile.ZIP_DEFLATED)
for filename in zin.namelist():
@@ -473,18 +553,25 @@ if __name__ == '__main__':
file = book.encryptedfiles[filename]
contents = file.decrypt(userkey, contents)
# Parse failures mean the key is probably wrong.
if not confirmedGood:
confirmedGood = file.check(contents)
file.check(contents)
zout.writestr(filename, contents)
zout.close()
print "Book saved as", os.path.join(os.getcwd(), outname)
print u"Decryption succeeded."
print u"Book saved as {0}".format(os.path.join(os.getcwd(), outname))
result = 0
break
except ValueError:
print "Decryption failed, trying next key"
print u"Decryption failed."
zout.close()
os.remove(outname)
zin.close()
lib.close()
exit(result)
if result != 0:
print u"Could not decrypt book with any of the keys found."
return result
if __name__ == '__main__':
sys.stdout=SafeUnbuffered(sys.stdout)
sys.stderr=SafeUnbuffered(sys.stderr)
sys.exit(cli_main())

View File

@@ -8,7 +8,7 @@
<body>
<h1>Obok DeDRM Plugin</h1>
<h3>(version 3.1.2)</h3>
<h3>(version 3.1.3)</h3>
<h3>Installation:</h3>

Binary file not shown.

View File

@@ -3,33 +3,37 @@
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-11-17 12:51+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"PO-Revision-Date: 2015-05-31 22:44+1000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Last-Translator: \n"
"Language-Team: \n"
"X-Generator: Poedit 1.8.1\n"
"Plural-Forms: nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5);\n"
"Language: ar\n"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:80
msgid ""
"<p>No books found in Kobo Library\n"
"Are you sure it's installed\\configured\\synchronized?"
msgstr ""
"<p>لا يوجد كتب بمكتبة كوبو الخاصة بكم\n"
"هل أنت متأكد أنها موجودة\\معدة بشكل سليم\\محملة بشكل كامل؟"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:87
msgid "Legacy key found: "
msgstr ""
msgstr "تم العثور على مفتاح شفرة قديم"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:93
msgid "Trouble retrieving keys with newer obok method."
msgstr ""
msgstr "هناك مشكلة فى تحميل مفاتيح الشفرة بطريقة أوبوك الجديدة."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:97
msgid "Found {0} possible keys to try."
@@ -150,13 +154,14 @@ msgstr ""
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:399
msgid "<p>{0} successfully added."
msgstr ""
msgstr "<p>{0} تم إضافتهم بنجاح."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:403
msgid ""
"<p>Not all selected Kobo books made it into calibre.<br /><br />View report "
"for details."
msgstr ""
"<p>لم يتم نقل كل كتب كوبو إلى كاليبر.<br/><br/>شاهد التقرير لمزيد من التفاصيل"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:404
msgid "<p><b>Total attempted:</b> {}</p>\n"
@@ -200,7 +205,7 @@ msgstr ""
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:458
msgid "Unknown Book Title"
msgstr ""
msgstr "عنوان غير معروف"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:460
msgid "it couldn't be decrypted."
@@ -214,15 +219,15 @@ msgstr ""
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:464
msgid "of unknown reasons. Gosh I'm embarrassed!"
msgstr ""
msgstr "معذرة، خطأ غير معروف! أشعر بالخجل من هذا!"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:465
msgid "<p>{0} not added because {1}"
msgstr ""
msgstr "<p>لم يتم إضافة {0} بسبب {1}"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\common_utils.py:226
msgid "Help"
msgstr ""
msgstr "مساعدة"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\common_utils.py:235
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\utilities.py:214
@@ -237,7 +242,7 @@ msgstr ""
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\common_utils.py:322
msgid "Undefined"
msgstr ""
msgstr "غير معرف"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\config.py:30
msgid "When should Obok try to insert EPUBs into existing calibre entries?"
@@ -251,32 +256,23 @@ msgstr ""
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\config.py:35
msgid "Ask"
msgstr ""
msgstr "اسأل"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\config.py:35
msgid "Always"
msgstr ""
msgstr "دائما"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\config.py:35
msgid "Never"
msgstr ""
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:60
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\utilities.py:150
msgid " v"
msgstr ""
msgstr "أبدا"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:65
msgid "Obok DeDRM"
msgstr ""
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:68
msgid "<a href=\"http://www.foo.com/\">Help</a>"
msgstr ""
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:89
msgid "Select All"
msgstr ""
msgstr "اختر الكل"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:90
msgid "Select all books to add them to the calibre library."
@@ -300,23 +296,23 @@ msgstr ""
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:146
msgid "Title"
msgstr ""
msgstr "العنوان"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:146
msgid "Author"
msgstr ""
msgstr "مؤلِّف"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:146
msgid "Series"
msgstr ""
msgstr "السلسلة"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:369
msgid "Copy to clipboard"
msgstr ""
msgstr "نسخ إلى الحافظة"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:397
msgid "View Report"
msgstr ""
msgstr "مشاهدة التقرير"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\__init__.py:21
msgid "Removes DRM from Kobo kepubs and adds them to the library."
@@ -328,8 +324,8 @@ msgstr ""
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\obok\obok.py:167
msgid "Failed to initialize AES key"
msgstr ""
msgstr "خطأ فى تحميل مفتاح AES"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\obok\obok.py:175
msgid "AES decryption failed"
msgstr ""
msgstr "خطأ فى فك الحماية بطريقة AES"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,361 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-11-17 12:51+0100\n"
"PO-Revision-Date: 2015-05-31 22:44+1000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Last-Translator: \n"
"Language-Team: \n"
"X-Generator: Poedit 1.8.1\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Language: pt\n"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:80
msgid ""
"<p>No books found in Kobo Library\n"
"Are you sure it's installed\\configured\\synchronized?"
msgstr ""
"<p>Não foram encontrados livros na livraria Kobo\n"
"Tem a certeza de que está instalado\\configured\\synchronized?"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:87
msgid "Legacy key found: "
msgstr "Chave de legado encontrada"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:93
msgid "Trouble retrieving keys with newer obok method."
msgstr "Problema na obtenção das chaves com o novo método obok."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:97
msgid "Found {0} possible keys to try."
msgstr "Encontradas {0} chaves possíveis para experimentar."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:99
msgid "<p>No userkeys found to decrypt books with. No point in proceeding."
msgstr ""
"Não foi encontrada nenhuma chave de usuário com a qual desencriptar os "
"livros. Não vale a pena continuar."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:115
msgid "{} - Decryption canceled by user."
msgstr "Desencriptação cancelada pelo utilizador."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:135
msgid "{} - \"Add books\" canceled by user."
msgstr "{} - \"Adição de livros\" cancelada pelo utilizador."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:137
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:156
msgid "{} - wrapping up results."
msgstr "{} - finalizando os resultados."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:153
msgid "{} - User opted not to try to insert EPUB formats"
msgstr "O utilizador optou por não tentar inserir formatos EPUB"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:188
msgid "{0} - Decrypting {1}"
msgstr "Desencriptando"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:197
msgid "{0} - Couldn't decrypt {1}"
msgstr "{0} - Não foi possível desencriptar {1}"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:198
msgid "decryption errors"
msgstr "erros na desencriptação"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:213
msgid "{0} - Added {1}"
msgstr "{0} - Adicionado {1}"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:218
msgid "{0} - {1} already exists. Will try to add format later."
msgstr "{0} - {1} já existe. a adição do formato irá ser tentada mais tarde."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:219
msgid "duplicate detected"
msgstr "detectados duplicados"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:233
msgid "{0} - Successfully added EPUB format to existing {1}"
msgstr "{0} - formato EPUB adicionado com sucesso ao existente {1}"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:236
msgid ""
"{0} - Error adding EPUB format to existing {1}. This really shouldn't happen."
msgstr ""
"{0} - Erro ao adicionar o formato EPUB ao existente {1}. Isto realmente não "
"deveria acontecer."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:259
msgid "{} - \"Insert formats\" canceled by user."
msgstr "{} - \"Inserção de formatos\" cancelada pelo utilizador."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:291
msgid ""
"<p><b>{0}</b> EPUB{2} successfully added to library.<br /><br /><b>{1}</b> "
msgstr ""
"<p><b>{0}</b> EPUB{2} adicionado com sucesso à biblioteca.<br /><br /><b>{1}"
"</b> "
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:292
msgid ""
"not added because books with the same title/author were detected.<br /><br /"
">Would you like to try and add the EPUB format{0}"
msgstr ""
"não adicionados porque foram detectados com o mesmo título/autor.<br /><br /"
">Gostaria de tentar e adicionar o formato EPUB{0}"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:293
msgid ""
" to those existing entries?<br /><br />NOTE: no pre-existing EPUBs will be "
"overwritten."
msgstr ""
" às entradas existentes?<br /><br />NOTA: EPUBs pré existentes não serão "
"reescritos."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:295
msgid ""
"{0} -- not added because of {1} in your library.\n"
"\n"
msgstr ""
"{0} -- não adicionado porque {1} na biblioteca.\n"
"\n"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:297
msgid "<p><b>{0}</b> -- not added because of {1} in your library.<br /><br />"
msgstr "<p><b>{0}</b> -- não adicionado porque {1} na biblioteca.<br /><br />"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:298
msgid ""
"Would you like to try and add the EPUB format to an available calibre "
"duplicate?<br /><br />"
msgstr ""
"Gostaria de tentar adicionar o formato EPUB a um duplicado já existente no "
"calibre?<br /><br />"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:299
msgid "NOTE: no pre-existing EPUB will be overwritten."
msgstr "NOTA: EPUBs pré existentes não serão reescritos."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:346
msgid "Trying key: "
msgstr "Experimentando a chave:"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:378
msgid "Decryption failed, trying next key."
msgstr "A desencriptação falhou, tentado a próxima chave."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:382
msgid "Unknown Error decrypting, trying next key.."
msgstr "Erro desconhecido na desencriptação, tentado a próxima chave."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:395
msgid ""
"<p>All selected Kobo books added as new calibre books or inserted into "
"existing calibre ebooks.<br /><br />No issues."
msgstr ""
"<p>Todos os livros Kobo selecionados foram adicionados como livros novos no "
"calibre ou inseridos em livros já existentes no calibre.<br /><br />Sem "
"problemas."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:399
msgid "<p>{0} successfully added."
msgstr "<p>{0} adicionados com sucesso."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:403
msgid ""
"<p>Not all selected Kobo books made it into calibre.<br /><br />View report "
"for details."
msgstr ""
"<p>Nem todos os livros Kobo selecionados seguiram para o calibre.<br /><br /"
">Veja o relatório para mais detalhes."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:404
msgid "<p><b>Total attempted:</b> {}</p>\n"
msgstr "<p><b>tentativas totais:</b> {}</p>\n"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:405
msgid "<p><b>Decryption errors:</b> {}</p>\n"
msgstr "<p><b>Erros de desencriptação:</b> {}</p>\n"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:411
msgid "<p><b>New Books created:</b> {}</p>\n"
msgstr "<p><b>Novos livros criados:</b> {}</p>\n"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:418
msgid "<p><b>Duplicates that weren't added:</b> {}</p>\n"
msgstr "<p><b>Duplicados não adicionados:</b> {}</p>\n"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:426
msgid "<p><b>Book imports cancelled by user:</b> {}</p>\n"
msgstr "<p><b>Importação de livros cancelada pelo utilizador:</b> {}</p>\n"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:428
msgid ""
"<p><b>New EPUB formats inserted in existing calibre books:</b> {0}</p>\n"
msgstr ""
"<p><b>Novos formatos EPUB inseridos em livros existentes no calibre:</b> {0}"
"</p>\n"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:434
msgid ""
"<p><b>EPUB formats NOT inserted into existing calibre books:</b> {}<br />\n"
msgstr ""
"<p><b>formatos EPUB NÃO inseridos em livros existentes no calibre:</b> {}"
"<br />\n"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:435
msgid ""
"(Either because the user <i>chose</i> not to insert them, or because all "
"duplicates already had an EPUB format)"
msgstr ""
"(Porque o utilizador <i>escolheu</i> não os inserir, ou porque todos os "
"duplicados já tinham um formato EPUB)"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:444
msgid "<p><b>Format imports cancelled by user:</b> {}</p>\n"
msgstr "<p><b>Importação do formato cancelada pelo utilizador:</b> {}</p>\n"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:458
msgid "Unknown Book Title"
msgstr "Título do livro desconhecido"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:460
msgid "it couldn't be decrypted."
msgstr "não pode ser desencriptado."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:462
msgid ""
"user CHOSE not to insert the new EPUB format, or all existing calibre "
"entries HAD an EPUB format already."
msgstr ""
"o utilizador ESCOLHEU não inserir o novo formato EPUB, ou todas as entradas "
"existentes no calibre já tinham um formato EPUB."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:464
msgid "of unknown reasons. Gosh I'm embarrassed!"
msgstr "de razões desconhecidas. Estou envergonhado!"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:465
msgid "<p>{0} not added because {1}"
msgstr "<p>{0} não adicionado porque {1}"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\common_utils.py:226
msgid "Help"
msgstr "Help"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\common_utils.py:235
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\utilities.py:214
msgid "Restart required"
msgstr "Reinicio requerido"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\common_utils.py:236
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\utilities.py:215
msgid ""
"Title image not found - you must restart Calibre before using this plugin!"
msgstr ""
"Imagem do título não encontrada - tem que reiniciar o Calibre antes de "
"utilizar este plugin!"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\common_utils.py:322
msgid "Undefined"
msgstr "Não definido"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\config.py:30
msgid "When should Obok try to insert EPUBs into existing calibre entries?"
msgstr ""
"Quando deve o Obok tentar inserir EPUBs em entradas já existentes no calibre?"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\config.py:33
msgid ""
"<p>Default behavior when duplicates are detected. None of the choices will "
"cause calibre ebooks to be overwritten"
msgstr ""
"<p>Comportamento por defeito quando são detetados duplicados. Nenhuma das "
"escolhas fará com que os livros existentes no calibre sejam reescritos"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\config.py:35
msgid "Ask"
msgstr "Pergunta"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\config.py:35
msgid "Always"
msgstr "Sempre"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\config.py:35
msgid "Never"
msgstr "Nunca"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:65
msgid "Obok DeDRM"
msgstr ""
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:89
msgid "Select All"
msgstr "Selecionar todos"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:90
msgid "Select all books to add them to the calibre library."
msgstr "Selecionar todos os livros para adicioná-los à biblioteca do calibre."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:92
msgid "All with DRM"
msgstr "Todos com DRM"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:93
msgid "Select all books with DRM."
msgstr "Selecionar todos os livros com DRM"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:95
msgid "All DRM free"
msgstr "Todos sem DRM"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:96
msgid "Select all books without DRM."
msgstr "Selecionar todos os livros sem DRM."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:146
msgid "Title"
msgstr "Título"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:146
msgid "Author"
msgstr "Autor"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:146
msgid "Series"
msgstr "Série"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:369
msgid "Copy to clipboard"
msgstr "Copiar para a área de transferência"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:397
msgid "View Report"
msgstr "Ver relatório"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\__init__.py:21
msgid "Removes DRM from Kobo kepubs and adds them to the library."
msgstr "Remove o DRM dos kepubs Kobo e adiciona-os à biblioteca."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\obok\obok.py:162
msgid "AES improper key used"
msgstr "AES chave imprópria usada"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\obok\obok.py:167
msgid "Failed to initialize AES key"
msgstr "Falha na inicialização da chave AES"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\obok\obok.py:175
msgid "AES decryption failed"
msgstr "A desencriptação da chave AES falhou"

View File

@@ -1,7 +1,7 @@
obok_plugin.zip
================
This plugin will remove the DRM from Kobo ebooks download on Mac or Windows using the Kobo desktop application.
This plugin will remove the DRM from Kobo ebooks download on Mac or Windows using the Kobo desktop application, or from Kobo ebooks on an attached Kobo reader. If both are available, ebooks will be read from the attached Kobo reader. To import from the desktop application, unplug the Kobo reader.
Installation
@@ -11,7 +11,7 @@ Do NOT select "Get plugins to enhance calibre" as this is reserved for 'official
Customization
-------------
No customization is required, except choosing which menus will show the plugin.
No customization is required, except choosing which menus will show the plugin. Altough the ability to enter a device serial number is given, this should not need to be filled in, as the serial number should be picked up automatically from the attached Kobo reader.
Using the plugin
@@ -47,6 +47,7 @@ Credits
-------
The original obok script was by Physisticated
The plugin conversion was done anonymously.
The Kobo reader support was added by norbusan
Improvements to the script and the plugin adaption have been by numerous people since.
Additional improvements to the script and the plugin adaption by numerous anonymous people.

View File

@@ -16,16 +16,18 @@ from __future__ import with_statement
# - and added in unicode command line support
# 1.3 - added in TkInter interface, output to a file
# 1.4 - Fix some problems identified by Aldo Bleeker
# 1.5 - Fix another problem identified by Aldo Bleeker
"""
Retrieve Kindle for Android Serial Number.
"""
__license__ = 'GPL v3'
__version__ = '1.4'
__version__ = '1.5'
import os
import sys
import traceback
import getopt
import tempfile
import zlib
@@ -220,20 +222,30 @@ def get_serials2(path=STORAGE2):
userdata_keys = cursor.fetchall()
dsns = []
for userdata_row in userdata_keys:
if userdata_row:
userdata_utf8 = userdata_row[0].encode('utf8')
if len(userdata_utf8) > 0:
dsns.append(userdata_utf8)
try:
if userdata_row and userdata_row[0]:
userdata_utf8 = userdata_row[0].encode('utf8')
if len(userdata_utf8) > 0:
dsns.append(userdata_utf8)
except:
print "Error getting one of the device serial name keys"
traceback.print_exc()
pass
dsns = list(set(dsns))
cursor.execute('''select userdata_value from userdata where userdata_key like '%/%kindle.account.tokens%' ''')
userdata_keys = cursor.fetchall()
tokens = []
for userdata_row in userdata_keys:
if userdata_row:
userdata_utf8 = userdata_row[0].encode('utf8')
if len(userdata_utf8) > 0:
tokens.append(userdata_utf8)
try:
if userdata_row and userdata_row[0]:
userdata_utf8 = userdata_row[0].encode('utf8')
if len(userdata_utf8) > 0:
tokens.append(userdata_utf8)
except:
print "Error getting one of the account token keys"
traceback.print_exc()
pass
tokens = list(set(tokens))
serials = []
@@ -377,7 +389,6 @@ def gui_main():
import Tkconstants
import tkMessageBox
import tkFileDialog
import traceback
except:
print "Tkinter not installed"
return cli_main()

View File

@@ -1,6 +1,20 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Version 3.1.6 September 2015
# Enable support for Kobo devices
# More character encoding fixes (unicode strings)
#
# Version 3.1.5 September 2015
# Removed requirement that a purchase has been made.
# Also add in character encoding fixes
#
# Version 3.1.4 September 2015
# Updated for version 3.17 of the Windows Desktop app.
#
# Version 3.1.3 August 2015
# Add translations for Portuguese and Arabic
#
# Version 3.1.2 January 2015
# Add coding, version number and version announcement
#
@@ -109,7 +123,7 @@
#
"""Manage all Kobo books, either encrypted or DRM-free."""
__version__ = '3.1.1'
__version__ = '3.1.6'
import sys
import os
@@ -209,6 +223,24 @@ def _load_crypto():
AES = _load_crypto()
# Wrap a stream so that output gets flushed immediately
# and also make sure that any unicode strings get
# encoded using "replace" before writing them.
class SafeUnbuffered:
def __init__(self, stream):
self.stream = stream
self.encoding = stream.encoding
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
if isinstance(data,unicode):
data = data.encode(self.encoding,"replace")
self.stream.write(data)
self.stream.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
class KoboLibrary(object):
"""The Kobo library.
@@ -216,23 +248,49 @@ class KoboLibrary(object):
written by the Kobo Desktop Edition application, including the list
of books, their titles, and the user's encryption key(s)."""
def __init__ (self):
print u"Obok v{0}\nCopyright © 2012-2014 Physisticated et al.".format(__version__)
if sys.platform.startswith('win'):
if sys.getwindowsversion().major > 5:
self.kobodir = os.environ['LOCALAPPDATA']
else:
self.kobodir = os.path.join(os.environ['USERPROFILE'], 'Local Settings', 'Application Data')
self.kobodir = os.path.join(self.kobodir, 'Kobo', 'Kobo Desktop Edition')
elif sys.platform.startswith('darwin'):
self.kobodir = os.path.join(os.environ['HOME'], 'Library', 'Application Support', 'Kobo', 'Kobo Desktop Edition')
self.bookdir = os.path.join(self.kobodir, 'kepub')
kobodb = os.path.join(self.kobodir, 'Kobo.sqlite')
self.__sqlite = sqlite3.connect(kobodb)
self.__cursor = self.__sqlite.cursor()
self._userkeys = []
self._books = []
self._volumeID = []
def __init__ (self, serials = [], device_path = None):
print u"Obok v{0}\nCopyright © 2012-2015 Physisticated et al.".format(__version__)
self.kobodir = u""
kobodb = u""
# - first check whether serials have been found or are provided
# and a device is connected. In this case, use the device
# - otherwise fall back to Kobo Desktop Application for Windows and Mac
if (device_path and (len(serials) > 0)):
self.kobodir = os.path.join(device_path, u".kobo")
# devices use KoboReader.sqlite
kobodb = os.path.join(self.kobodir, u"KoboReader.sqlite")
if (not(os.path.exists(kobodb))):
# give up here, we haven't found anything useful
self.kobodir = u""
kobodb = u""
if (self.kobodir == u""):
# we haven't found a device with serials, so try desktop apps
if sys.platform.startswith('win'):
import _winreg as winreg
if sys.getwindowsversion().major > 5:
if 'LOCALAPPDATA' in os.environ.keys():
# Python 2.x does not return unicode env. Use Python 3.x
self.kobodir = winreg.ExpandEnvironmentStrings(u"%LOCALAPPDATA%")
if (self.kobodir == u""):
if 'USERPROFILE' in os.environ.keys():
# Python 2.x does not return unicode env. Use Python 3.x
self.kobodir = os.path.join(winreg.ExpandEnvironmentStrings(u"%USERPROFILE%"), u"Local Settings", u"Application Data")
self.kobodir = os.path.join(self.kobodir, u"Kobo", u"Kobo Desktop Edition")
elif sys.platform.startswith('darwin'):
self.kobodir = os.path.join(os.environ['HOME'], u"Library", u"Application Support", u"Kobo", u"Kobo Desktop Edition")
# desktop versions use Kobo.sqlite
kobodb = os.path.join(self.kobodir, u"Kobo.sqlite")
if (self.kobodir != u""):
self.bookdir = os.path.join(self.kobodir, u"kepub")
self.__sqlite = sqlite3.connect(kobodb)
self.__cursor = self.__sqlite.cursor()
self._userkeys = []
self._books = []
self._volumeID = []
self._serials = serials
def close (self):
"""Closes the database used by the library."""
@@ -246,9 +304,8 @@ class KoboLibrary(object):
"""
if len(self._userkeys) != 0:
return self._userkeys
userid = self.__getuserid()
for macaddr in self.__getmacaddrs():
self._userkeys.append(self.__getuserkey(macaddr, userid))
self._userkeys.extend(self.__getuserkeys(macaddr))
return self._userkeys
@property
@@ -274,7 +331,7 @@ class KoboLibrary(object):
def __bookfile (self, volumeid):
"""The filename needed to open a given book."""
return os.path.join(self.kobodir, 'kepub', volumeid)
return os.path.join(self.kobodir, u"kepub", volumeid)
def __getmacaddrs (self):
"""The list of all MAC addresses on this machine."""
@@ -290,17 +347,42 @@ class KoboLibrary(object):
output = subprocess.check_output('/sbin/ifconfig -a', shell=True)
matches = c.findall(output)
for m in matches:
# print "m:",m[0]
# print u"m:{0}".format(m[0])
macaddrs.append(m[0].upper())
# extend the list of macaddrs in any case with the serials
# cannot hurt ;-)
macaddrs.extend(self._serials)
return macaddrs
def __getuserid (self):
return self.__cursor.execute('SELECT UserID FROM user WHERE HasMadePurchase = "true"').fetchone()[0]
def __getuserkey (self, macaddr, userid):
def __getuserids (self):
userids = []
cursor = self.__cursor.execute('SELECT UserID FROM user')
row = cursor.fetchone()
while row is not None:
try:
userid = row[0]
userids.append(userid)
except:
pass
row = cursor.fetchone()
return userids
def __getuserkeys (self, macaddr):
userids = self.__getuserids()
userkeys = []
# This version is used for versions before 3.17.0.
deviceid = hashlib.sha256('NoCanLook' + macaddr).hexdigest()
userkey = hashlib.sha256(deviceid + userid).hexdigest()
return binascii.a2b_hex(userkey[32:])
for userid in userids:
userkey = hashlib.sha256(deviceid + userid).hexdigest()
userkeys.append(binascii.a2b_hex(userkey[32:]))
# This version is used for 3.17.0 and later.
deviceid = hashlib.sha256('XzUhGYdFp' + macaddr).hexdigest()
for userid in userids:
userkey = hashlib.sha256(deviceid + userid).hexdigest()
userkeys.append(binascii.a2b_hex(userkey[32:]))
return userkeys
class KoboBook(object):
"""A Kobo book.
@@ -407,13 +489,13 @@ class KoboFile(object):
if contents[:5]=="<?xml":
return True
else:
print "Bad XML: ",contents[:5]
print u"Bad XML: {0}".format(contents[:5])
raise ValueError
if self.mimetype == 'image/jpeg':
if contents[:3] == '\xff\xd8\xff':
return True
else:
print "Bad JPEG: ", contents[:3].encode('hex')
print u"Bad JPEG: {0}".format(contents[:3].encode('hex'))
raise ValueError()
return False
@@ -435,36 +517,34 @@ class KoboFile(object):
contents = contents[:-padding]
return contents
if __name__ == '__main__':
def cli_main():
lib = KoboLibrary()
for i, book in enumerate(lib.books):
print ('%d: %s' % (i + 1, book.title)).encode('ascii', 'ignore')
print u"{0}: {1}".format(i + 1, book.title)
num_string = raw_input("Convert book number... ")
num_string = raw_input(u"Convert book number... ")
try:
num = int(num_string)
book = lib.books[num - 1]
except (ValueError, IndexError):
exit()
print "Converting", book.title
print u"Converting {0}".format(book.title)
zin = zipfile.ZipFile(book.filename, "r")
# make filename out of Unicode alphanumeric and whitespace equivalents from title
outname = "%s.epub" % (re.sub('[^\s\w]', '', book.title, 0, re.UNICODE))
outname = u"{0}.epub".format(re.sub('[^\s\w]', '_', book.title, 0, re.UNICODE))
if (book.type == 'drm-free'):
print "DRM-free book, conversion is not needed"
print u"DRM-free book, conversion is not needed"
shutil.copyfile(book.filename, outname)
print "Book saved as", os.path.join(os.getcwd(), outname)
print u"Book saved as {0}".format(os.path.join(os.getcwd(), outname))
exit(0)
result = 1
for userkey in lib.userkeys:
# print "Trying key: ",userkey.encode('hex_codec')
confirmedGood = False
print u"Trying key: {0}".format(userkey.encode('hex_codec'))
try:
zout = zipfile.ZipFile(outname, "w", zipfile.ZIP_DEFLATED)
for filename in zin.namelist():
@@ -473,18 +553,25 @@ if __name__ == '__main__':
file = book.encryptedfiles[filename]
contents = file.decrypt(userkey, contents)
# Parse failures mean the key is probably wrong.
if not confirmedGood:
confirmedGood = file.check(contents)
file.check(contents)
zout.writestr(filename, contents)
zout.close()
print "Book saved as", os.path.join(os.getcwd(), outname)
print u"Decryption succeeded."
print u"Book saved as {0}".format(os.path.join(os.getcwd(), outname))
result = 0
break
except ValueError:
print "Decryption failed, trying next key"
print u"Decryption failed."
zout.close()
os.remove(outname)
zin.close()
lib.close()
exit(result)
if result != 0:
print u"Could not decrypt book with any of the keys found."
return result
if __name__ == '__main__':
sys.stdout=SafeUnbuffered(sys.stdout)
sys.stderr=SafeUnbuffered(sys.stderr)
sys.exit(cli_main())

View File

@@ -1,7 +1,7 @@
Welcome to the tools!
=====================
This ReadMe_First.txt is meant to give users a quick overview of what is available and how to get started. This document is part of the Tools v6.3.1 archive from Apprentice Alf's Blog: http://apprenticealf.wordpress.com/
This ReadMe_First.txt is meant to give users a quick overview of what is available and how to get started. This document is part of the Tools v6.3.4 archive from Apprentice Alf's Blog: http://apprenticealf.wordpress.com/
The is archive includes tools to remove DRM from: