tools v5.2

This commit is contained in:
Apprentice Alf 2012-09-09 01:45:24 +01:00
parent 0812438b9d
commit f3f02adc98
75 changed files with 2556 additions and 1092 deletions

View File

@ -1,40 +1,38 @@
Ignoble Epub DeDRM - ignobleepub_vXX_plugin.zip Ignoble Epub DeDRM - ignobleepub_v01.6_plugin.zip
Requires Calibre version 0.6.44 or higher.
All credit given to I♥Cabbages for the original standalone scripts.
All credit given to I <3 Cabbages for the original standalone scripts. I had the much easier job of converting them to a calibre plugin.
I had the much easier job of converting them to a Calibre plugin.
This plugin is meant to decrypt Barnes & Noble Epubs that are protected with Adobe's Adept encryption. It is meant to function without having to install any dependencies... other than having calibre installed, of course. It will still work if you have Python and PyCrypto already installed, but they aren't necessary.
This plugin is meant to decrypt Barnes & Noble Epubs that are protected
with Adobe's Adept encryption. It is meant to function without having to install any dependencies... other than having Calibre installed, of course. It will still work if you have Python and PyCrypto already installed, but they aren't necessary.
Installation:
Installation:
Go to calibre's Preferences page. Do **NOT** select "Get plugins to enhance calibre" as this is reserved for "official" calibre plugins, instead select "Change calibre behavior". Under "Advanced" click on the Plugins button. Use the "Load plugin from file" button to select the plugin's zip file (ignobleepub_vXX_plugin.zip) and click the 'Add' button. you're done.
Go to Calibre's Preferences page. Do **NOT** select "Get plugins to enhance calibre" as this is reserved for "official" calibre plugins, instead select "Change calibre behavior". Under "Advanced" click on the Plugins button. Use the "Load plugin from file" button to select the plugin's zip file (ignobleepub_vXX_plugin.zip) and
click the 'Add' button. you're done. Please note: calibre does not provide any immediate feedback to indicate that adding the plugin was a success. You can always click on the File-Type plugins to see if the plugin was added.
Please note: Calibre does not provide any immediate feedback to indicate that adding the plugin was a success. You can always click on the File-Type plugins to see if the plugin was added.
Configuration:
Configuration:
1) The easiest way to configure the plugin is to enter your name (Barnes & Noble account name) and credit card number (the one used to purchase the books) into the plugin's customization window. It's the same info you would enter into the ignoblekeygen script. Highlight the plugin (Ignoble Epub DeDRM) and click the "Customize Plugin" button on calibre's Preferences->Plugins page. Enter the name and credit card number separated by a comma: Your Name,1234123412341234
1) The easiest way to configure the plugin is to enter your name (Barnes & Noble account name) and credit card number (the one used to purchase the books) into the plugin's customization window. It's the same info you would enter into the ignoblekeygen script. Highlight the plugin (Ignoble Epub DeDRM) and click the "Customize Plugin" button on
Calibre's Preferences->Plugins page. Enter the name and credit card number separated by a comma: Your Name,1234123412341234 If you've purchased books with more than one credit card, separate that other info with a colon: Your Name,1234123412341234:Other Name,2345234523452345
If you've purchased books with more than one credit card, separate that other info with a colon: Your Name,1234123412341234:Other Name,2345234523452345 ** NOTE ** The above method is your only option if you don't have/can't run the original I♥Cabbages scripts on your particular machine. Your credit card number will be on display in calibre's Plugin configuration page when using the above method. If other people have access to your computer, you may want to use the second configuration method below.
** NOTE ** The above method is your only option if you don't have/can't run the original I <3 Cabbages scripts on your particular machine.
2) If you already have keyfiles generated with I <3 Cabbages' ignoblekeygen.pyw script, you can put those keyfiles into calibre's configuration directory. The easiest way to find the correct directory is to go to calibre's Preferences page... click on the 'Miscellaneous' button (looks like a gear), and then click the 'Open calibre configuration directory' button. Paste your keyfiles in there. Just make sure that they have different names and are saved with the '.b64' extension (like the ignoblekeygen script produces). This directory isn't touched when upgrading calibre, so it's quite safe to leave them there.
** NOTE ** Your credit card number will be on display in Calibre's Plugin configuration page when using the above method. If other people have access to your computer, you may want to use the second configuration method below.
All keyfiles from method 2 and all data entered from method 1 will be used to attempt to decrypt a book. You can use method 1 or method 2, or a combination of both.
2) If you already have keyfiles generated with I <3 Cabbages' ignoblekeygen.pyw script, you can put those keyfiles into Calibre's configuration directory. The easiest way to find the correct directory is to go to Calibre's Preferences page... click on the 'Miscellaneous' button (looks like a gear), and then click the 'Open Calibre
configuration directory' button. Paste your keyfiles in there. Just make sure that they have different names and are saved with the '.b64' extension (like the ignoblekeygen script produces). This directory isn't touched when upgrading Calibre, so it's quite safe to leave them there.
Troubleshooting:
All keyfiles from method 2 and all data entered from method 1 will be used to attempt to decrypt a book. You can use method 1 or method 2, or a combination of both.
If you find that it's not working for you (imported epubs still have DRM), you can save a lot of time and trouble by trying to add the epub to calibre with the command line tools. This will print out a lot of helpful debugging info that can be copied into any online help requests. I'm going to ask you to do it first, anyway, so you might as well get used to it. ;)
Troubleshooting:
Open a command prompt (terminal) and change to the directory where the ebook you're trying to import resides. Then type the command "calibredb add your_ebook.epub". Don't type the quotes and obviously change the 'your_ebook.epub' to whatever the filename of your book is. Copy the resulting output and paste it into any online help request you make.
If you find that it's not working for you (imported epubs still have DRM), you can save a lot of time and trouble by trying to add the epub to Calibre with the command line tools. This will print out a lot of helpful debugging info that can be copied into any online help requests. I'm going to ask you to do it first, anyway, so you might
as well get used to it. ;) ** Note: the Mac version of calibre doesn't install the command line tools by default. If you go to the 'Preferences' page and click on the miscellaneous button, you'll see the option to install the command line tools.
Open a command prompt (terminal) and change to the directory where the ebook you're trying to import resides. Then type the command "calibredb add your_ebook.epub". Don't type the quotes and obviously change the 'your_ebook.epub' to whatever the filename of your book is. Copy the resulting output and paste it into any online help request you make.
** Note: the Mac version of Calibre doesn't install the command line tools by default. If you go to the 'Preferences' page and click on the miscellaneous button, you'll see the option to install the command line tools.

View File

@ -1,39 +1,39 @@
Inept Epub DeDRM - ineptepub_vXX_plugin.zip Inept Epub DeDRM - ineptepub_v01.7_plugin.zip
Requires Calibre version 0.6.44 or higher.
All credit given to I♥Cabbages for the original standalone scripts.
All credit given to I <3 Cabbages for the original standalone scripts. I had the much easier job of converting them to a Calibre plugin.
I had the much easier job of converting them to a Calibre plugin.
This plugin is meant to decrypt Adobe Digital Edition Epubs that are protected with Adobe's Adept encryption. It is meant to function without having to install any dependencies... other than having Calibre installed, of course. It will still work if you have Python and PyCrypto already installed, but they aren't necessary.
This plugin is meant to decrypt Adobe Digital Edition Epubs that are protected with Adobe's Adept encryption. It is meant to function without having to install any dependencies... other than having Calibre installed, of course. It will still work if you have Python and PyCrypto already installed, but they aren't necessary.
Installation: Installation:
Go to Calibre's Preferences page. Do **NOT** select "Get plugins to enhance calibre" as this is reserved for "official" calibre plugins, instead select "Cahnge calibre behavior". Under "Advanced" click on the Plugins button. Use the "Load plugin from file" button to select the plugin's zip file (ineptepub_vXX_plugin.zip) and click the 'Add' button. you're done. Go to Calibre's Preferences page. Do **NOT** select "Get plugins to enhance calibre" as this is reserved for "official" calibre plugins, instead select "Cahnge calibre behavior". Under "Advanced" click on the Plugins button. Use the "Load plugin from file" button to select the plugin's zip file (ineptepub_vXX_plugin.zip) and click the 'Add' button. you're done.
Please note: Calibre does not provide any immediate feedback to indicate that adding the plugin was a success. You can always click on the File-Type plugins to see if the plugin was added. Please note: Calibre does not provide any immediate feedback to indicate that adding the plugin was a success. You can always click on the File-Type plugins to see if the plugin was added.
Configuration: Configuration:
When first run, the plugin will attempt to find your Adobe Digital Editions installation (on Windows and Mac OS's). If successful, it will create an 'adeptkey.der' file and save it in Calibre's configuration directory. It will use that file on subsequent runs. If there are already '*.der' files in the directory, the plugin won't attempt to When first run, the plugin will attempt to find your Adobe Digital Editions installation (on Windows and Mac OS's). If successful, it will create an 'adeptkey.der' file and save it in Calibre's configuration directory. It will use that file on subsequent runs. If there are already '*.der' files in the directory, the plugin won't attempt to find the Adobe Digital Editions installation installation.
find the Adobe Digital Editions installation installation.
So if you have Adobe Digital Editions installation installed on the same machine as Calibre... you are ready to go. If not... keep reading.
So if you have Adobe Digital Editions installation installed on the same machine as Calibre... you are ready to go. If not... keep reading.
If you already have keyfiles generated with I♥Cabbages' ineptkey.pyw script, you can put those keyfiles in Calibre's configuration directory. The easiest way to find the correct directory is to go to Calibre's Preferences page... click on the 'Miscellaneous' button (looks like a gear), and then click the 'Open Calibre configuration directory' button. Paste your keyfiles in there. Just make sure that they have different names and are saved with the '.der' extension (like the ineptkey script produces). This directory isn't touched when upgrading Calibre, so it's quite safe to leave them there.
If you already have keyfiles generated with I <3 Cabbages' ineptkey.pyw script, you can put those keyfiles in Calibre's configuration directory. The easiest way to find the correct directory is to go to Calibre's Preferences page... click on the 'Miscellaneous' button (looks like a gear), and then click the 'Open Calibre configuration directory' button. Paste your keyfiles in there. Just make sure that
they have different names and are saved with the '.der' extension (like the ineptkey script produces). This directory isn't touched when upgrading Calibre, so it's quite safe to leave them there. Since there is no Linux version of Adobe Digital Editions, Linux users will have to obtain a keyfile through other methods and put the file in Calibre's configuration directory.
Since there is no Linux version of Adobe Digital Editions, Linux users will have to obtain a keyfile through other methods and put the file in Calibre's configuration directory. All keyfiles with a '.der' extension found in Calibre's configuration directory will be used to attempt to decrypt a book.
All keyfiles with a '.der' extension found in Calibre's configuration directory will be used to attempt to decrypt a book.
** NOTE ** There is no plugin customization data for the Inept Epub DeDRM plugin.
** NOTE ** There is no plugin customization data for the Inept Epub DeDRM plugin.
Troubleshooting: Troubleshooting:
If you find that it's not working for you (imported epubs still have DRM), you can save a lot of time and trouble by trying to add the epub to Calibre with the command line tools. This will print out a lot of helpful debugging info that can be copied into any online help requests. I'm going to ask you to do it first, anyway, so you might If you find that it's not working for you (imported epubs still have DRM), you can save a lot of time and trouble by trying to add the epub to Calibre with the command line tools. This will print out a lot of helpful debugging info that can be copied into any online help requests. I'm going to ask you to do it first, anyway, so you might as well get used to it. ;)
as well get used to it. ;)
Open a command prompt (terminal) and change to the directory where the ebook you're trying to import resides. Then type the command "calibredb add your_ebook.epub". Don't type the quotes and obviously change the 'your_ebook.epub' to whatever the filename of your book is. Copy the resulting output and paste it into any online help request you make.
Open a command prompt (terminal) and change to the directory where the ebook you're trying to import resides. Then type the command "calibredb add your_ebook.epub". Don't type the quotes and obviously change the 'your_ebook.epub' to whatever the filename of your book is. Copy the resulting output and paste it into any online help request you make.
** Note: the Mac version of Calibre doesn't install the command line tools by default. If you go to the 'Preferences' page and click on the miscellaneous button, you'll see the option to install the command line tools.
** Note: the Mac version of Calibre doesn't install the command line tools by default. If you go to the 'Preferences' page and click on the miscellaneous button, you'll see the option to install the command line tools.

View File

@ -1,39 +1,39 @@
Inept PDF Plugin - ineptpdf_vXX_plugin.zip Inept PDF Plugin - ineptpdf_v01.5_plugin.zip
Requires Calibre version 0.6.44 or higher.
All credit given to I♥Cabbages for the original standalone scripts.
All credit given to IHeartCabbages for the original standalone scripts. I had the much easier job of converting them to a Calibre plugin.
I had the much easier job of converting them to a Calibre plugin.
This plugin is meant to decrypt Adobe Digital Edition PDFs that are protected with Adobe's Adept encryption. It is meant to function without having to install any dependencies... other than having Calibre installed, of course. It will still work if you have Python, PyCrypto and/or OpenSSL already installed, but they aren't necessary.
This plugin is meant to decrypt Adobe Digital Edition PDFs that are protected with Adobe's Adept encryption. It is meant to function without having to install any dependencies... other than having Calibre installed, of course. It will still work if you have Python, PyCrypto and/or OpenSSL already installed, but they aren't necessary.
Installation: Installation:
Go to Calibre's Preferences page. Do **NOT** select "Get plugins to enhance calibre" as this is reserved for "official" plugins, instead select "Change calibre behavior". Under "Advanced" click on the Plugins button. Use the "Load plugin from file" button to select the plugin's zip file (ineptpdf_vXX_plugin.zip) and click the 'Add' button. you're done. Go to Calibre's Preferences page. Do **NOT** select "Get plugins to enhance calibre" as this is reserved for "official" plugins, instead select "Change calibre behavior". Under "Advanced" click on the Plugins button. Use the "Load plugin from file" button to select the plugin's zip file (ineptpdf_vXX_plugin.zip) and click the 'Add' button. you're done.
Please note: Calibre does not provide any immediate feedback to indicate that adding the plugin was a success. You can always click on the File-Type plugins to see if the plugin was added. Please note: Calibre does not provide any immediate feedback to indicate that adding the plugin was a success. You can always click on the File-Type plugins to see if the plugin was added.
Configuration: Configuration:
When first run, the plugin will attempt to find your Adobe Digital Editions installation (on Windows and Mac OS's). If successful, it will create an 'adeptkey.der' file and save it in Calibre's configuration directory. It will use that file on subsequent runs. If there are already '*.der' files in the directory, the plugin won't attempt to When first run, the plugin will attempt to find your Adobe Digital Editions installation (on Windows and Mac OS's). If successful, it will create an 'adeptkey.der' file and save it in Calibre's configuration directory. It will use that file on subsequent runs. If there are already '*.der' files in the directory, the plugin won't attempt to find the Adobe Digital Editions installation installation.
find the Adobe Digital Editions installation installation.
So if you have Adobe Digital Editions installation installed on the same machine as Calibre... you are ready to go. If not... keep reading.
So if you have Adobe Digital Editions installation installed on the same machine as Calibre... you are ready to go. If not... keep reading.
If you already have keyfiles generated with I <3 Cabbages' ineptkey.pyw script, you can put those keyfiles in Calibre's configuration directory. The easiest way to find the correct directory is to go to Calibre's Preferences page... click on the 'Miscellaneous' button (looks like a gear), and then click the 'Open Calibre configuration directory' button. Paste your keyfiles in there. Just make sure that
If you already have keyfiles generated with I <3 Cabbages' ineptkey.pyw script, you can put those keyfiles in Calibre's configuration directory. The easiest way to find the correct directory is to go to Calibre's Preferences page... click on the 'Miscellaneous' button (looks like a gear), and then click the 'Open Calibre configuration directory' button. Paste your keyfiles in there. Just make sure that they have different names and are saved with the '.der' extension (like the ineptkey script produces). This directory isn't touched when upgrading Calibre, so it's quite safe to leave them there.
they have different names and are saved with the '.der' extension (like the ineptkey script produces). This directory isn't touched when upgrading Calibre, so it's quite safe to leave them there.
Since there is no Linux version of Adobe Digital Editions, Linux users will have to obtain a keyfile through other methods and put the file in Calibre's configuration directory.
Since there is no Linux version of Adobe Digital Editions, Linux users will have to obtain a keyfile through other methods and put the file in Calibre's configuration directory.
All keyfiles with a '.der' extension found in Calibre's configuration directory will be used to attempt to decrypt a book.
All keyfiles with a '.der' extension found in Calibre's configuration directory will be used to attempt to decrypt a book.
** NOTE ** There is no plugin customization data for the Inept PDF plugin.
** NOTE ** There is no plugin customization data for the Inept PDF plugin.
Troubleshooting: Troubleshooting:
If you find that it's not working for you (imported PDFs still have DRM), you can save a lot of time and trouble by trying to add the PDF to Calibre with the command line tools. This will print out a lot of helpful debugging info that can be copied into any online help requests. I'm going to ask you to do it first, anyway, so you might If you find that it's not working for you (imported PDFs still have DRM), you can save a lot of time and trouble by trying to add the PDF to Calibre with the command line tools. This will print out a lot of helpful debugging info that can be copied into any online help requests. I'm going to ask you to do it first, anyway, so you might as well get used to it. ;)
as well get used to it. ;)
Open a command prompt (terminal) and change to the directory where the ebook you're trying to import resides. Then type the command "calibredb add your_ebook.pdf". Don't type the quotes and obviously change the 'your_ebook.pdf' to whatever the filename of your book is. Copy the resulting output and paste it into any online help request you make.
Open a command prompt (terminal) and change to the directory where the ebook you're trying to import resides. Then type the command "calibredb add your_ebook.pdf". Don't type the quotes and obviously change the 'your_ebook.pdf' to whatever the filename of your book is. Copy the resulting output and paste it into any online help request you make.
** Note: the Mac version of Calibre doesn't install the command line tools by default. If you go to the 'Preferences' page and click on the miscellaneous button, you'll see the option to install the command line tools.
** Note: the Mac version of Calibre doesn't install the command line tools by default. If you go to the 'Preferences' page and click on the miscellaneous button, you'll see the option to install the command line tools.

View File

@ -1,22 +1,37 @@
Plugin for K4PC, K4Mac, standalone Kindles, Mobi Books, and for Devices with Fixed PIDs. K4MobiDeDRM_v04.4_plugin.zip
This plugin supersedes MobiDeDRM, K4DeDRM, and K4PCDeDRM and K4X plugins. If you install this plugin, those plugins can be safely removed. Credit given to The Dark Reverser for the original standalone script. Credit also to the many people who have updated and expanded that script since then.
This plugin is meant to remove the DRM from .prc, .azw, .azw1, and .tpz ebooks. Calibre can then convert them to whatever format you desire. It is meant to function without having to install any dependencies except for Calibre being on your same machine and in the same account as your "Kindle for PC" or "Kindle for Mac" application if you are going to remove the DRM from those types of books. Plugin for K4PC, K4Mac, eInk Kindles and Mobipocket.
Installation: This plugin supersedes MobiDeDRM, K4DeDRM, and K4PCDeDRM and K4X plugins. If you install this plugin, those plugins can be safely removed.
Go to Calibre's Preferences page. Do **NOT** select "Get Plugins to enhance calibre" as this is reserved for official calibre plugins", instead select "Change calibre behavior". Under "Advanced" click on the on the Plugins button. Click on the "Load plugin from file" button at the bottom of the screen. Use the file dialog button to select the plugin's zip file (K4MobiDeDRM_vXX_plugin.zip) and click the "Add" (or it may say "Open" button. Then click on the "Yes" button in the warning dialog that appears. A Confirmation dialog appears that says the plugin has been installed.
This plugin is meant to remove the DRM from .prc, .mobi, .azw, .azw1, .azw3, .azw4 and .tpz ebooks. Calibre can then convert them to whatever format you desire. It is meant to function without having to install any dependencies except for Calibre being on your same machine and in the same account as your "Kindle for PC" or "Kindle for Mac" application if you are going to remove the DRM from those types of books.
Configuration:
Highlight the plugin (K4MobiDeDRM under the "File type plugins" category) and click the "Customize Plugin" button on Calibre's Preferences->Plugins page. Enter your 10 digit PID. If you have more than one PID separate them with a comma (no spaces). If you have a standalone Kindle include the 16 digit serial number (these typically begin "B0...") in this list (again separated from the PIDs or other serial numbers with a comma (no spaces). This configuration is not needed if you only want to decode "Kindle for PC" or "Kindle for Mac" books. Installation:
Go to Calibre's Preferences page. Do **NOT** select "Get Plugins to enhance calibre" as this is reserved for official calibre plugins", instead select "Change calibre behavior". Under "Advanced" click on the on the Plugins button. Click on the "Load plugin from file" button at the bottom of the screen. Use the file dialog button to select the plugin's zip file (K4MobiDeDRM_vXX_plugin.zip) and click the "Add" (or it may say "Open" button. Then click on the "Yes" button in the warning dialog that appears. A Confirmation dialog appears that says the plugin has been installed.
Troubleshooting:
If you find that it's not working for you, you can save a lot of time and trouble by trying to add the azw file to Calibre with the command line tools. This will print out a lot of helpful debugging info that can be copied into any online help requests. I'm going to ask you to do it first, anyway, so you might
as well get used to it. ;) Configuration:
Open a command prompt (terminal) and change to the directory where the ebook you're trying to import resides. Then type the command "calibredb add your_ebook.azw". Don't type the quotes and obviously change the 'your_ebook.azw' to whatever the filename of your book is. Copy the resulting output and paste it into any online help request you make. Highlight the plugin (K4MobiDeDRM under the "File type plugins" category) and click the "Customize Plugin" button on Calibre's Preferences->Plugins page. If you have an eInk Kindle enter the 16 digit serial number (these typically begin "B0..."). If you have more than one eInk Kindle, you can enter multiple serial numbers separated by commas (no spaces). If you have Mobipocket books, enter your 10 digit PID. If you have more than one PID, separate them with commax (no spaces).
** Note: the Mac version of Calibre doesn't install the command line tools by default. If you go to the 'Preferences' page and click on the miscellaneous button, you'll see the option to install the command line tools. This configuration step is not needed if you only want to decode "Kindle for PC" or "Kindle for Mac" books.
Linux Systems Only:
If you install Kindle for PC in Wine, the plugin should be able to decode files from that Kindle for PC installation under Wine. You might need to enter a Wine Prefix if it's not already set in your Environment variables.
Troubleshooting:
If you find that it's not working for you, you can save a lot of time and trouble by trying to add the DRMed ebook to Calibre with the command line tools. This will print out a lot of helpful debugging info that can be copied into any online help requests. I'm going to ask you to do it first, anyway, so you might as well get used to it. ;)
Open a command prompt (terminal) and change to the directory where the ebook you're trying to import resides. Then type the command "calibredb add your_ebook_file". Don't type the quotes and obviously change the 'your_ebook_file' to whatever the filename of your book is (including any file name extension like .azw). Copy the resulting output and paste it into any online help request you make.
** Note: the Mac version of Calibre doesn't install the command line tools by default. If you go to the 'Preferences' page and click on the miscellaneous button, you'll see the option to install the command line tools.

View File

@ -15,16 +15,16 @@ import re
from zipfile import ZipFile from zipfile import ZipFile
class K4DeDRM(FileTypePlugin): class K4DeDRM(FileTypePlugin):
name = 'K4PC, K4Mac, Kindle Mobi and Topaz DeDRM' # Name of the plugin name = 'Kindle and Mobipocket DeDRM' # Name of the plugin
description = 'Removes DRM from Mobipocket, Kindle/Mobi, Kindle/Topaz and Kindle/Print Replica files. Provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc.' description = 'Removes DRM from eInk Kindle, Kindle 4 Mac and Kindle 4 PC ebooks, and from Mobipocket ebooks. Provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, mdlnx, ApprenticeAlf, etc.'
supported_platforms = ['osx', 'windows', 'linux'] # Platforms this plugin will run on supported_platforms = ['osx', 'windows', 'linux'] # Platforms this plugin will run on
author = 'DiapDealer, SomeUpdates' # The author of this plugin author = 'DiapDealer, SomeUpdates, mdlnx, Apprentice Alf' # The author of this plugin
version = (0, 4, 2) # The version number of this plugin version = (0, 4, 4) # The version number of this plugin
file_types = set(['prc','mobi','azw','azw1','azw3','azw4','tpz']) # The file types that this plugin will be applied to file_types = set(['prc','mobi','azw','azw1','azw3','azw4','tpz']) # The file types that this plugin will be applied to
on_import = True # Run this plugin during the import on_import = True # Run this plugin during the import
priority = 210 # run this plugin before mobidedrm, k4pcdedrm, k4dedrm priority = 520 # run this plugin before earlier versions
minimum_calibre_version = (0, 7, 55) minimum_calibre_version = (0, 7, 55)
def initialize(self): def initialize(self):
""" """
Dynamic modules can't be imported/loaded from a zipfile... so this routine Dynamic modules can't be imported/loaded from a zipfile... so this routine
@ -39,7 +39,7 @@ class K4DeDRM(FileTypePlugin):
elif isosx: elif isosx:
names = ['libalfcrypto.dylib'] names = ['libalfcrypto.dylib']
else: else:
names = ['libalfcrypto32.so','libalfcrypto64.so'] names = ['libalfcrypto32.so','libalfcrypto64.so','alfcrypto.py','alfcrypto.dll','alfcrypto64.dll','getk4pcpids.py','mobidedrm.py','kgenpids.py','k4pcutils.py','topazextract.py']
lib_dict = self.load_resources(names) lib_dict = self.load_resources(names)
self.alfdir = os.path.join(config_dir, 'alfcrypto') self.alfdir = os.path.join(config_dir, 'alfcrypto')
if not os.path.exists(self.alfdir): if not os.path.exists(self.alfdir):
@ -62,35 +62,45 @@ class K4DeDRM(FileTypePlugin):
plug_ver = '.'.join(str(self.version).strip('()').replace(' ', '').split(',')) plug_ver = '.'.join(str(self.version).strip('()').replace(' ', '').split(','))
k4 = True k4 = True
if sys.platform.startswith('linux'):
k4 = False
pids = [] pids = []
serials = [] serials = []
kInfoFiles = [] kInfoFiles = []
self.config()
# Get supplied list of PIDs to try from plugin customization. # Get supplied list of PIDs to try from plugin customization.
customvalues = self.site_customization.split(',') pidstringlistt = self.pids_string.split(',')
for customvalue in customvalues: for pid in pidstringlistt:
customvalue = str(customvalue) pid = str(pid).strip()
customvalue = customvalue.strip() if len(pid) == 10 or len(pid) == 8:
if len(customvalue) == 10 or len(customvalue) == 8: pids.append(pid)
pids.append(customvalue) else:
else : if len(pid) > 0:
if len(customvalue) == 16 and customvalue[0] == 'B': print "'%s' is not a valid Mobipocket PID." % pid
serials.append(customvalue)
else: # For linux, get PIDs by calling the right routines under WINE
print "%s is not a valid Kindle serial number or PID." % str(customvalue) if sys.platform.startswith('linux'):
k4 = False
pids.extend(self.WINEgetPIDs(path_to_ebook))
# Get supplied list of Kindle serial numbers to try from plugin customization.
serialstringlistt = self.serials_string.split(',')
for serial in serialstringlistt:
serial = str(serial).strip()
if len(serial) == 16 and serial[0] == 'B':
serials.append(serial)
else:
if len(serial) > 0:
print "'%s' is not a valid Kindle serial number." % serial
# Load any kindle info files (*.info) included Calibre's config directory. # Load any kindle info files (*.info) included Calibre's config directory.
try: try:
# Find Calibre's configuration directory. print 'K4MobiDeDRM v%s: Calibre configuration directory = %s' % (plug_ver, config_dir)
confpath = os.path.split(os.path.split(self.plugin_path)[0])[0] files = os.listdir(config_dir)
print 'K4MobiDeDRM v%s: Calibre configuration directory = %s' % (plug_ver, confpath)
files = os.listdir(confpath)
filefilter = re.compile("\.info$|\.kinf$", re.IGNORECASE) filefilter = re.compile("\.info$|\.kinf$", re.IGNORECASE)
files = filter(filefilter.search, files) files = filter(filefilter.search, files)
if files: if files:
for filename in files: for filename in files:
fpath = os.path.join(confpath, filename) fpath = os.path.join(config_dir, filename)
kInfoFiles.append(fpath) kInfoFiles.append(fpath)
print 'K4MobiDeDRM v%s: Kindle info/kinf file %s found in config folder.' % (plug_ver, filename) print 'K4MobiDeDRM v%s: Kindle info/kinf file %s found in config folder.' % (plug_ver, filename)
except IOError: except IOError:
@ -152,8 +162,77 @@ class K4DeDRM(FileTypePlugin):
mb.cleanup() mb.cleanup()
return of.name return of.name
def customization_help(self, gui=False): def WINEgetPIDs(self, infile):
return 'Enter 10 character PIDs and/or Kindle serial numbers, use a comma (no spaces) to separate each PID or SerialNumber from the next.'
import subprocess
from subprocess import Popen, PIPE, STDOUT
import subasyncio
from subasyncio import Process
print " Getting PIDs from WINE"
outfile = os.path.join(self.alfdir + 'winepids.txt')
cmdline = 'wine python.exe ' \
+ '"'+self.alfdir + '/getk4pcpids.py"' \
+ ' "' + infile + '"' \
+ ' "' + outfile + '"'
env = os.environ
print "My wine_prefix from tweaks is ", self.wine_prefix
if ("WINEPREFIX" in env):
print "Using WINEPREFIX from the environment: ", env["WINEPREFIX"]
elif (self.wine_prefix is not None):
env['WINEPREFIX'] = self.wine_prefix
print "Using WINEPREFIX from tweaks: ", self.wine_prefix
else:
print "No wine prefix used"
print cmdline
cmdline = cmdline.encode(sys.getfilesystemencoding())
p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=sys.stdout, stderr=STDOUT, close_fds=False)
result = p2.wait("wait")
print "Conversion returned ", result
WINEpids = []
customvalues = file(outfile, 'r').readline().split(',')
for customvalue in customvalues:
customvalue = str(customvalue)
customvalue = customvalue.strip()
if len(customvalue) == 10 or len(customvalue) == 8:
WINEpids.append(customvalue)
else:
print "'%s' is not a valid PID." % customvalue
return WINEpids
def is_customizable(self):
# return true to allow customization via the Plugin->Preferences.
return True
def config_widget(self):
# It is important to put this import statement here rather than at the
# top of the module as importing the config class will also cause the
# GUI libraries to be loaded, which we do not want when using calibre
# from the command line
from calibre_plugins.k4mobidedrm.config import ConfigWidget
return config.ConfigWidget()
def config(self):
from calibre_plugins.k4mobidedrm.config import prefs
self.pids_string = prefs['pids']
self.serials_string = prefs['serials']
self.wine_prefix = prefs['WINEPREFIX']
def save_settings(self, config_widget):
'''
Save the settings specified by the user with config_widget.
'''
config_widget.save_settings()
self.config()
def load_resources(self, names): def load_resources(self, names):
ans = {} ans = {}
@ -161,4 +240,4 @@ class K4DeDRM(FileTypePlugin):
for candidate in zf.namelist(): for candidate in zf.namelist():
if candidate in names: if candidate in names:
ans[candidate] = zf.read(candidate) ans[candidate] = zf.read(candidate)
return ans return ans

View File

@ -0,0 +1,59 @@
from PyQt4.Qt import QWidget, QVBoxLayout, QLabel, QLineEdit
from calibre.utils.config import JSONConfig
# This is where all preferences for this plugin will be stored
# You should always prefix your config file name with plugins/,
# so as to ensure you dont accidentally clobber a calibre config file
prefs = JSONConfig('plugins/K4MobiDeDRM')
# Set defaults
prefs.defaults['pids'] = ""
prefs.defaults['serials'] = ""
prefs.defaults['WINEPREFIX'] = None
class ConfigWidget(QWidget):
def __init__(self):
QWidget.__init__(self)
self.l = QVBoxLayout()
self.setLayout(self.l)
self.serialLabel = QLabel('Kindle Serial numbers (separate with commas, no spaces)')
self.l.addWidget(self.serialLabel)
self.serials = QLineEdit(self)
self.serials.setText(prefs['serials'])
self.l.addWidget(self.serials)
self.serialLabel.setBuddy(self.serials)
self.pidLabel = QLabel('Mobipocket PIDs (separate with commas, no spaces)')
self.l.addWidget(self.pidLabel)
self.pids = QLineEdit(self)
self.pids.setText(prefs['pids'])
self.l.addWidget(self.pids)
self.pidLabel.setBuddy(self.serials)
self.wpLabel = QLabel('For Linux only: WINEPREFIX (enter absolute path)')
self.l.addWidget(self.wpLabel)
self.wineprefix = QLineEdit(self)
wineprefix = prefs['WINEPREFIX']
if wineprefix is not None:
self.wineprefix.setText(wineprefix)
else:
self.wineprefix.setText('')
self.l.addWidget(self.wineprefix)
self.wpLabel.setBuddy(self.wineprefix)
def save_settings(self):
prefs['pids'] = str(self.pids.text())
prefs['serials'] = str(self.serials.text())
winepref=str(self.wineprefix.text())
if winepref.strip() != '':
prefs['WINEPREFIX'] = winepref
else:
prefs['WINEPREFIX'] = None

View File

@ -214,6 +214,7 @@ class PageParser(object):
'links.title' : (1, 'text', 0, 0), 'links.title' : (1, 'text', 0, 0),
'links.href' : (1, 'text', 0, 0), 'links.href' : (1, 'text', 0, 0),
'links.type' : (1, 'text', 0, 0), 'links.type' : (1, 'text', 0, 0),
'links.id' : (1, 'number', 0, 0),
'paraCont' : (0, 'number', 1, 1), 'paraCont' : (0, 'number', 1, 1),
'paraCont.rootID' : (1, 'number', 0, 0), 'paraCont.rootID' : (1, 'number', 0, 0),
@ -239,6 +240,7 @@ class PageParser(object):
'group' : (1, 'snippets', 1, 0), 'group' : (1, 'snippets', 1, 0),
'group.type' : (1, 'scalar_text', 0, 0), 'group.type' : (1, 'scalar_text', 0, 0),
'group._tag' : (1, 'scalar_text', 0, 0), 'group._tag' : (1, 'scalar_text', 0, 0),
'group.orientation': (1, 'scalar_text', 0, 0),
'region' : (1, 'snippets', 1, 0), 'region' : (1, 'snippets', 1, 0),
'region.type' : (1, 'scalar_text', 0, 0), 'region.type' : (1, 'scalar_text', 0, 0),
@ -246,7 +248,7 @@ class PageParser(object):
'region.y' : (1, 'scalar_number', 0, 0), 'region.y' : (1, 'scalar_number', 0, 0),
'region.h' : (1, 'scalar_number', 0, 0), 'region.h' : (1, 'scalar_number', 0, 0),
'region.w' : (1, 'scalar_number', 0, 0), 'region.w' : (1, 'scalar_number', 0, 0),
'region.orientation' : (1, 'scalar_number', 0, 0), 'region.orientation' : (1, 'scalar_text', 0, 0),
'empty_text_region' : (1, 'snippets', 1, 0), 'empty_text_region' : (1, 'snippets', 1, 0),

View File

@ -0,0 +1,77 @@
#!/usr/bin/python
#
# This is a python script. You need a Python interpreter to run it.
# For example, ActiveState Python, which exists for windows.
#
# Changelog
# 1.00 - Initial version
__version__ = '1.00'
import sys
class Unbuffered:
def __init__(self, stream):
self.stream = stream
def write(self, data):
self.stream.write(data)
self.stream.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
sys.stdout=Unbuffered(sys.stdout)
import os
import struct
import binascii
import kgenpids
import topazextract
import mobidedrm
from alfcrypto import Pukall_Cipher
class DrmException(Exception):
pass
def getK4PCpids(path_to_ebook):
# Return Kindle4PC PIDs. Assumes that the caller checked that we are not on Linux, which will raise an exception
mobi = True
magic3 = file(path_to_ebook,'rb').read(3)
if magic3 == 'TPZ':
mobi = False
if mobi:
mb = mobidedrm.MobiBook(path_to_ebook,False)
else:
mb = topazextract.TopazBook(path_to_ebook)
md1, md2 = mb.getPIDMetaInfo()
return kgenpids.getPidList(md1, md2, True, [], [], [])
def main(argv=sys.argv):
print ('getk4pcpids.py v%(__version__)s. '
'Copyright 2012 Apprentic Alf' % globals())
if len(argv)<2 or len(argv)>3:
print "Gets the possible book-specific PIDs from K4PC for a particular book"
print "Usage:"
print " %s <bookfile> [<outfile>]" % sys.argv[0]
return 1
else:
infile = argv[1]
try:
pidlist = getK4PCpids(infile)
except DrmException, e:
print "Error: %s" % e
return 1
pidstring = ','.join(pidlist)
print "Possible PIDs are: ", pidstring
if len(argv) is 3:
outfile = argv[2]
file(outfile, 'w').write(pidstring)
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@ -17,7 +17,7 @@ from __future__ import with_statement
# and many many others # and many many others
__version__ = '4.2' __version__ = '4.3'
class Unbuffered: class Unbuffered:
def __init__(self, stream): def __init__(self, stream):
@ -58,7 +58,7 @@ else:
# borrowed from calibre from calibre/src/calibre/__init__.py # borrowed from calibre from calibre/src/calibre/__init__.py
# added in removal of non-printing chars # added in removal of non-printing chars
# and removal of . at start # and removal of . at start
# convert spaces to underscores # convert underscores to spaces (we're OK with spaces in file names)
def cleanup_name(name): def cleanup_name(name):
_filename_sanitize = re.compile(r'[\xae\0\\|\?\*<":>\+/]') _filename_sanitize = re.compile(r'[\xae\0\\|\?\*<":>\+/]')
substitute='_' substitute='_'
@ -73,7 +73,7 @@ def cleanup_name(name):
# Mac and Unix don't like file names that begin with a full stop # Mac and Unix don't like file names that begin with a full stop
if len(one) > 0 and one[0] == '.': if len(one) > 0 and one[0] == '.':
one = substitute+one[1:] one = substitute+one[1:]
one = one.replace(' ','_') one = one.replace('_',' ')
return one return one
def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids): def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
@ -99,11 +99,20 @@ def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
title = mb.getBookTitle() title = mb.getBookTitle()
print "Processing Book: ", title print "Processing Book: ", title
filenametitle = cleanup_name(title) filenametitle = cleanup_name(title)
outfilename = bookname outfilename = cleanup_name(bookname)
if len(outfilename)<=8 or len(filenametitle)<=8:
outfilename = outfilename + "_" + filenametitle # generate 'sensible' filename, that will sort with the original name,
elif outfilename[:8] != filenametitle[:8]: # but is close to the name from the file.
outfilename = outfilename[:8] + "_" + filenametitle outlength = len(outfilename)
comparelength = min(8,min(outlength,len(filenametitle)))
copylength = min(max(outfilename.find(' '),8),len(outfilename))
if outlength==0:
outfilename = filenametitle
elif comparelength > 0:
if outfilename[:comparelength] == filenametitle[:comparelength]:
outfilename = filenametitle
else:
outfilename = outfilename[:copylength] + " " + filenametitle
# avoid excessively long file names # avoid excessively long file names
if len(outfilename)>150: if len(outfilename)>150:

View File

@ -262,9 +262,15 @@ def getPidList(md1, md2, k4, pids, serials, kInfoFiles):
if k4: if k4:
kInfoFiles = getKindleInfoFiles(kInfoFiles) kInfoFiles = getKindleInfoFiles(kInfoFiles)
for infoFile in kInfoFiles: for infoFile in kInfoFiles:
pidlst = getK4Pids(pidlst, md1, md2, infoFile) try:
pidlst = getK4Pids(pidlst, md1, md2, infoFile)
except Exception, message:
print("Error getting PIDs from " + infoFile + ": " + message)
for serialnum in serials: for serialnum in serials:
pidlst = getKindlePid(pidlst, md1, md2, serialnum) try:
pidlst = getKindlePid(pidlst, md1, md2, serialnum)
except Exception, message:
print("Error getting PIDs from " + serialnum + ": " + message)
for pid in pids: for pid in pids:
pidlst.append(pid) pidlst.append(pid)
return pidlst return pidlst

View File

@ -164,6 +164,9 @@ class DocParser(object):
scale = self.pw scale = self.pw
elif attr == 'line-space': elif attr == 'line-space':
scale = self.fontsize * 2.0 scale = self.fontsize * 2.0
if val == "":
val = 0
if not ((attr == 'hang') and (int(val) == 0)) : if not ((attr == 'hang') and (int(val) == 0)) :
pv = float(val)/scale pv = float(val)/scale

View File

@ -31,11 +31,8 @@ class TpzDRMError(Exception):
# local support routines # local support routines
if inCalibre: if inCalibre:
from calibre_plugins.k4mobidedrm import kgenpids from calibre_plugins.k4mobidedrm import kgenpids
from calibre_plugins.k4mobidedrm import genbook
else: else:
import kgenpids import kgenpids
import genbook
# recursive zip creation support routine # recursive zip creation support routine
def zipUpDir(myzip, tdir, localname): def zipUpDir(myzip, tdir, localname):
@ -271,6 +268,11 @@ class TopazBook:
self.createBookDirectory() self.createBookDirectory()
self.extractFiles() self.extractFiles()
print "Successfully Extracted Topaz contents" print "Successfully Extracted Topaz contents"
if inCalibre:
from calibre_plugins.k4mobidedrm import genbook
else:
import genbook
rv = genbook.generateBook(self.outdir, raw, fixedimage) rv = genbook.generateBook(self.outdir, raw, fixedimage)
if rv == 0: if rv == 0:
print "\nBook Successfully generated" print "\nBook Successfully generated"
@ -300,6 +302,11 @@ class TopazBook:
self.createBookDirectory() self.createBookDirectory()
self.extractFiles() self.extractFiles()
print "Successfully Extracted Topaz contents" print "Successfully Extracted Topaz contents"
if inCalibre:
from calibre_plugins.k4mobidedrm import genbook
else:
import genbook
rv = genbook.generateBook(self.outdir, raw, fixedimage) rv = genbook.generateBook(self.outdir, raw, fixedimage)
if rv == 0: if rv == 0:
print "\nBook Successfully generated" print "\nBook Successfully generated"

View File

@ -1,23 +1,31 @@
eReader PDB2PML - eReaderPDB2PML_vXX_plugin.zip eReader PDB2PML - eReaderPDB2PML_v06_plugin.zip
All credit given to The Dark Reverser for the original standalone script. I had the much easier job of converting it to a Calibre plugin. All credit given to The Dark Reverser for the original standalone script. I had the much easier job of converting it to a Calibre plugin.
This plugin is meant to convert secure Ereader files (PDB) to unsecured PMLZ files. Calibre can then convert it to whatever format you desire. It is meant to function without having to install any dependencies... other than having Calibre installed, of course. I've included the psyco libraries (compiled for each platform) for speed. If your system can use them, great! Otherwise, they won't be used and things will just work slower. This plugin is meant to convert secure Ereader files (PDB) to unsecured PMLZ files. Calibre can then convert it to whatever format you desire. It is meant to function without having to install any dependencies... other than having Calibre installed, of course. I've included the psyco libraries (compiled for each platform) for speed. If your system can use them, great! Otherwise, they won't be used and things will just work slower.
Installation:
Go to Calibre's Preferences page. Do **NOT** select "Get Plugins to enhance calibre" as this is reserved for "official" calibre plugins, instead select "Change calibre behavior". Under "Advanced" click on the Plugins button. Use the "Load plugin from file" button to select the plugin's zip file (eReaderPDB2PML_vXX_plugin.zip) and click the 'Add' button. You're done. Installation:
Please note: Calibre does not provide any immediate feedback to indicate that adding the plugin was a success. You can always click on the File-Type plugins to see if the plugin was added. Go to Calibre's Preferences page. Do **NOT** select "Get Plugins to enhance calibre" as this is reserved for "official" calibre plugins, instead select "Change calibre behavior". Under "Advanced" click on the Plugins button. Use the "Load plugin from file" button to select the plugin's zip file (eReaderPDB2PML_vXX_plugin.zip) and click the 'Add' button. You're done.
Configuration:
Highlight the plugin (eReader PDB 2 PML under the "File type plugins" category) and click the "Customize Plugin" button on Calibre's Preferences->Plugins page. Enter your name and last 8 digits of the credit card number separated by a comma: Your Name,12341234 Please note: Calibre does not provide any immediate feedback to indicate that adding the plugin was a success. You can always click on the File-Type plugins to see if the plugin was added.
If you've purchased books with more than one credit card, separate the info with a colon: Your Name,12341234:Other Name,23452345 (NOTE: Do NOT put quotes around your name like you do with the original script!!)
Configuration:
Troubleshooting:
If you find that it's not working for you (imported pdb's are not converted to pmlz format), you can save a lot of time and trouble by trying to add the pdb to Calibre with the command line tools. This will print out a lot of helpful debugging info that can be copied into any online help requests. I'm going to ask you to do it first, anyway, so you might Highlight the plugin (eReader PDB 2 PML under the "File type plugins" category) and click the "Customize Plugin" button on Calibre's Preferences->Plugins page. Enter your name and last 8 digits of the credit card number separated by a comma: Your Name,12341234
as well get used to it. ;)
If you've purchased books with more than one credit card, separate the info with a colon: Your Name,12341234:Other Name,23452345 (NOTE: Do NOT put quotes around your name like you do with the original script!!)
Open a command prompt (terminal) and change to the directory where the ebook you're trying to import resides. Then type the command "calibredb add your_ebook.pdb". Don't type the quotes and obviously change the 'your_ebook.pdb' to whatever the filename of your book is. Copy the resulting output and paste it into any online help request you make.
** Note: the Mac version of Calibre doesn't install the command line tools by default. If you go to the 'Preferences' page and click on the miscellaneous button, you'll see the option to install the command line tools. Troubleshooting:
If you find that it's not working for you (imported pdb's are not converted to pmlz format), you can save a lot of time and trouble by trying to add the pdb to Calibre with the command line tools. This will print out a lot of helpful debugging info that can be copied into any online help requests. I'm going to ask you to do it first, anyway, so you might as well get used to it. ;)
Open a command prompt (terminal) and change to the directory where the ebook you're trying to import resides. Then type the command "calibredb add your_ebook.pdb". Don't type the quotes and obviously change the 'your_ebook.pdb' to whatever the filename of your book is. Copy the resulting output and paste it into any online help request you make.
** Note: the Mac version of Calibre doesn't install the command line tools by default. If you go to the 'Preferences' page and click on the miscellaneous button, you'll see the option to install the command line tools.

Binary file not shown.

View File

@ -1,6 +1,8 @@
#! /usr/bin/env python #! /usr/bin/env python
# ineptpdf plugin __init__.py, version 0.1.5
from __future__ import with_statement
# ineptpdf plugin __init__.py
# Released under the terms of the GNU General Public Licence, version 3 or # Released under the terms of the GNU General Public Licence, version 3 or
# later. <http://www.gnu.org/licenses/> # later. <http://www.gnu.org/licenses/>
@ -52,13 +54,11 @@
# 0.1.2 - back port ineptpdf 8.4.X bug fixes # 0.1.2 - back port ineptpdf 8.4.X bug fixes
# 0.1.3 - add in fix for improper rejection of session bookkeys with len(bookkey) = length + 1 # 0.1.3 - add in fix for improper rejection of session bookkeys with len(bookkey) = length + 1
# 0.1.4 - update to the new calibre plugin interface # 0.1.4 - update to the new calibre plugin interface
# 0.1.5 - synced to ineptpdf 7.11
""" """
Decrypts Adobe ADEPT-encrypted PDF files. Decrypts Adobe ADEPT-encrypted PDF files.
""" """
from __future__ import with_statement
__license__ = 'GPL v3' __license__ = 'GPL v3'
import sys import sys
@ -116,13 +116,13 @@ def _load_crypto_libcrypto():
class RSA(Structure): class RSA(Structure):
pass pass
RSA_p = POINTER(RSA) RSA_p = POINTER(RSA)
def F(restype, name, argtypes): def F(restype, name, argtypes):
func = getattr(libcrypto, name) func = getattr(libcrypto, name)
func.restype = restype func.restype = restype
func.argtypes = argtypes func.argtypes = argtypes
return func return func
AES_cbc_encrypt = F(None, 'AES_cbc_encrypt',[c_char_p, c_char_p, c_ulong, AES_KEY_p, c_char_p,c_int]) AES_cbc_encrypt = F(None, 'AES_cbc_encrypt',[c_char_p, c_char_p, c_ulong, AES_KEY_p, c_char_p,c_int])
AES_set_decrypt_key = F(c_int, 'AES_set_decrypt_key',[c_char_p, c_int, AES_KEY_p]) AES_set_decrypt_key = F(c_int, 'AES_set_decrypt_key',[c_char_p, c_int, AES_KEY_p])
@ -143,7 +143,7 @@ def _load_crypto_libcrypto():
rsa = self._rsa = d2i_RSAPrivateKey(None, pp, len(der)) rsa = self._rsa = d2i_RSAPrivateKey(None, pp, len(der))
if rsa is None: if rsa is None:
raise ADEPTError('Error parsing ADEPT user key DER') raise ADEPTError('Error parsing ADEPT user key DER')
def decrypt(self, from_): def decrypt(self, from_):
rsa = self._rsa rsa = self._rsa
to = create_string_buffer(RSA_size(rsa)) to = create_string_buffer(RSA_size(rsa))
@ -152,7 +152,7 @@ def _load_crypto_libcrypto():
if dlen < 0: if dlen < 0:
raise ADEPTError('RSA decryption failed') raise ADEPTError('RSA decryption failed')
return to[1:dlen] return to[1:dlen]
def __del__(self): def __del__(self):
if self._rsa is not None: if self._rsa is not None:
RSA_free(self._rsa) RSA_free(self._rsa)
@ -214,13 +214,13 @@ def _load_crypto_pycrypto():
# ASN.1 parsing code from tlslite # ASN.1 parsing code from tlslite
class ASN1Error(Exception): class ASN1Error(Exception):
pass pass
class ASN1Parser(object): class ASN1Parser(object):
class Parser(object): class Parser(object):
def __init__(self, bytes): def __init__(self, bytes):
self.bytes = bytes self.bytes = bytes
self.index = 0 self.index = 0
def get(self, length): def get(self, length):
if self.index + length > len(self.bytes): if self.index + length > len(self.bytes):
raise ASN1Error("Error decoding ASN.1") raise ASN1Error("Error decoding ASN.1")
@ -230,22 +230,22 @@ def _load_crypto_pycrypto():
x |= self.bytes[self.index] x |= self.bytes[self.index]
self.index += 1 self.index += 1
return x return x
def getFixBytes(self, lengthBytes): def getFixBytes(self, lengthBytes):
bytes = self.bytes[self.index : self.index+lengthBytes] bytes = self.bytes[self.index : self.index+lengthBytes]
self.index += lengthBytes self.index += lengthBytes
return bytes return bytes
def getVarBytes(self, lengthLength): def getVarBytes(self, lengthLength):
lengthBytes = self.get(lengthLength) lengthBytes = self.get(lengthLength)
return self.getFixBytes(lengthBytes) return self.getFixBytes(lengthBytes)
def getFixList(self, length, lengthList): def getFixList(self, length, lengthList):
l = [0] * lengthList l = [0] * lengthList
for x in range(lengthList): for x in range(lengthList):
l[x] = self.get(length) l[x] = self.get(length)
return l return l
def getVarList(self, length, lengthLength): def getVarList(self, length, lengthLength):
lengthList = self.get(lengthLength) lengthList = self.get(lengthLength)
if lengthList % length != 0: if lengthList % length != 0:
@ -255,19 +255,19 @@ def _load_crypto_pycrypto():
for x in range(lengthList): for x in range(lengthList):
l[x] = self.get(length) l[x] = self.get(length)
return l return l
def startLengthCheck(self, lengthLength): def startLengthCheck(self, lengthLength):
self.lengthCheck = self.get(lengthLength) self.lengthCheck = self.get(lengthLength)
self.indexCheck = self.index self.indexCheck = self.index
def setLengthCheck(self, length): def setLengthCheck(self, length):
self.lengthCheck = length self.lengthCheck = length
self.indexCheck = self.index self.indexCheck = self.index
def stopLengthCheck(self): def stopLengthCheck(self):
if (self.index - self.indexCheck) != self.lengthCheck: if (self.index - self.indexCheck) != self.lengthCheck:
raise ASN1Error("Error decoding ASN.1") raise ASN1Error("Error decoding ASN.1")
def atLengthCheck(self): def atLengthCheck(self):
if (self.index - self.indexCheck) < self.lengthCheck: if (self.index - self.indexCheck) < self.lengthCheck:
return False return False
@ -275,13 +275,13 @@ def _load_crypto_pycrypto():
return True return True
else: else:
raise ASN1Error("Error decoding ASN.1") raise ASN1Error("Error decoding ASN.1")
def __init__(self, bytes): def __init__(self, bytes):
p = self.Parser(bytes) p = self.Parser(bytes)
p.get(1) p.get(1)
self.length = self._getASN1Length(p) self.length = self._getASN1Length(p)
self.value = p.getFixBytes(self.length) self.value = p.getFixBytes(self.length)
def getChild(self, which): def getChild(self, which):
p = self.Parser(self.value) p = self.Parser(self.value)
for x in range(which+1): for x in range(which+1):
@ -290,7 +290,7 @@ def _load_crypto_pycrypto():
length = self._getASN1Length(p) length = self._getASN1Length(p)
p.getFixBytes(length) p.getFixBytes(length)
return ASN1Parser(p.bytes[markIndex:p.index]) return ASN1Parser(p.bytes[markIndex:p.index])
def _getASN1Length(self, p): def _getASN1Length(self, p):
firstLength = p.get(1) firstLength = p.get(1)
if firstLength<=127: if firstLength<=127:
@ -311,6 +311,7 @@ def _load_crypto_pycrypto():
return self._arc4.decrypt(data) return self._arc4.decrypt(data)
class AES(object): class AES(object):
MODE_CBC = _AES.MODE_CBC
@classmethod @classmethod
def new(cls, userkey, mode, iv): def new(cls, userkey, mode, iv):
self = AES() self = AES()
@ -333,7 +334,7 @@ def _load_crypto_pycrypto():
for byte in bytes: for byte in bytes:
total = (total << 8) + byte total = (total << 8) + byte
return total return total
def decrypt(self, data): def decrypt(self, data):
return self._rsa.decrypt(data) return self._rsa.decrypt(data)
@ -426,7 +427,7 @@ class PSLiteral(PSObject):
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
return return
def __repr__(self): def __repr__(self):
name = [] name = []
for char in self.name: for char in self.name:
@ -445,22 +446,22 @@ class PSKeyword(PSObject):
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
return return
def __repr__(self): def __repr__(self):
return self.name return self.name
# PSSymbolTable # PSSymbolTable
class PSSymbolTable(object): class PSSymbolTable(object):
''' '''
Symbol table that stores PSLiteral or PSKeyword. Symbol table that stores PSLiteral or PSKeyword.
''' '''
def __init__(self, classe): def __init__(self, classe):
self.dic = {} self.dic = {}
self.classe = classe self.classe = classe
return return
def intern(self, name): def intern(self, name):
if name in self.dic: if name in self.dic:
lit = self.dic[name] lit = self.dic[name]
@ -530,11 +531,11 @@ class PSBaseParser(object):
def flush(self): def flush(self):
return return
def close(self): def close(self):
self.flush() self.flush()
return return
def tell(self): def tell(self):
return self.bufpos+self.charpos return self.bufpos+self.charpos
@ -570,7 +571,7 @@ class PSBaseParser(object):
raise PSEOF('Unexpected EOF') raise PSEOF('Unexpected EOF')
self.charpos = 0 self.charpos = 0
return return
def parse_main(self, s, i): def parse_main(self, s, i):
m = NONSPC.search(s, i) m = NONSPC.search(s, i)
if not m: if not m:
@ -605,11 +606,11 @@ class PSBaseParser(object):
return (self.parse_wclose, j+1) return (self.parse_wclose, j+1)
self.add_token(KWD(c)) self.add_token(KWD(c))
return (self.parse_main, j+1) return (self.parse_main, j+1)
def add_token(self, obj): def add_token(self, obj):
self.tokens.append((self.tokenstart, obj)) self.tokens.append((self.tokenstart, obj))
return return
def parse_comment(self, s, i): def parse_comment(self, s, i):
m = EOL.search(s, i) m = EOL.search(s, i)
if not m: if not m:
@ -620,7 +621,7 @@ class PSBaseParser(object):
# We ignore comments. # We ignore comments.
#self.tokens.append(self.token) #self.tokens.append(self.token)
return (self.parse_main, j) return (self.parse_main, j)
def parse_literal(self, s, i): def parse_literal(self, s, i):
m = END_LITERAL.search(s, i) m = END_LITERAL.search(s, i)
if not m: if not m:
@ -634,7 +635,7 @@ class PSBaseParser(object):
return (self.parse_literal_hex, j+1) return (self.parse_literal_hex, j+1)
self.add_token(LIT(self.token)) self.add_token(LIT(self.token))
return (self.parse_main, j) return (self.parse_main, j)
def parse_literal_hex(self, s, i): def parse_literal_hex(self, s, i):
c = s[i] c = s[i]
if HEX.match(c) and len(self.hex) < 2: if HEX.match(c) and len(self.hex) < 2:
@ -669,7 +670,7 @@ class PSBaseParser(object):
self.token += s[i:j] self.token += s[i:j]
self.add_token(float(self.token)) self.add_token(float(self.token))
return (self.parse_main, j) return (self.parse_main, j)
def parse_keyword(self, s, i): def parse_keyword(self, s, i):
m = END_KEYWORD.search(s, i) m = END_KEYWORD.search(s, i)
if not m: if not m:
@ -817,7 +818,7 @@ class PSStackParser(PSBaseParser):
PSBaseParser.__init__(self, fp) PSBaseParser.__init__(self, fp)
self.reset() self.reset()
return return
def reset(self): def reset(self):
self.context = [] self.context = []
self.curtype = None self.curtype = None
@ -858,10 +859,10 @@ class PSStackParser(PSBaseParser):
def do_keyword(self, pos, token): def do_keyword(self, pos, token):
return return
def nextobject(self, direct=False): def nextobject(self, direct=False):
''' '''
Yields a list of objects: keywords, literals, strings, Yields a list of objects: keywords, literals, strings,
numbers, arrays and dictionaries. Arrays and dictionaries numbers, arrays and dictionaries. Arrays and dictionaries
are represented as Python sequence and dictionaries. are represented as Python sequence and dictionaries.
''' '''
@ -930,7 +931,7 @@ class PDFNotImplementedError(PSException): pass
## PDFObjRef ## PDFObjRef
## ##
class PDFObjRef(PDFObject): class PDFObjRef(PDFObject):
def __init__(self, doc, objid, genno): def __init__(self, doc, objid, genno):
if objid == 0: if objid == 0:
if STRICT: if STRICT:
@ -1045,25 +1046,25 @@ def stream_value(x):
# ascii85decode(data) # ascii85decode(data)
def ascii85decode(data): def ascii85decode(data):
n = b = 0 n = b = 0
out = '' out = ''
for c in data: for c in data:
if '!' <= c and c <= 'u': if '!' <= c and c <= 'u':
n += 1 n += 1
b = b*85+(ord(c)-33) b = b*85+(ord(c)-33)
if n == 5: if n == 5:
out += struct.pack('>L',b) out += struct.pack('>L',b)
n = b = 0 n = b = 0
elif c == 'z': elif c == 'z':
assert n == 0 assert n == 0
out += '\0\0\0\0' out += '\0\0\0\0'
elif c == '~': elif c == '~':
if n: if n:
for _ in range(5-n): for _ in range(5-n):
b = b*85+84 b = b*85+84
out += struct.pack('>L',b)[:n-1] out += struct.pack('>L',b)[:n-1]
break break
return out return out
## PDFStream type ## PDFStream type
@ -1080,7 +1081,7 @@ class PDFStream(PDFObject):
else: else:
if eol in ('\r', '\n', '\r\n'): if eol in ('\r', '\n', '\r\n'):
rawdata = rawdata[:length] rawdata = rawdata[:length]
self.dic = dic self.dic = dic
self.rawdata = rawdata self.rawdata = rawdata
self.decipher = decipher self.decipher = decipher
@ -1094,7 +1095,7 @@ class PDFStream(PDFObject):
self.objid = objid self.objid = objid
self.genno = genno self.genno = genno
return return
def __repr__(self): def __repr__(self):
if self.rawdata: if self.rawdata:
return '<PDFStream(%r): raw=%d, %r>' % \ return '<PDFStream(%r): raw=%d, %r>' % \
@ -1178,7 +1179,7 @@ class PDFStream(PDFObject):
data = self.decipher(self.objid, self.genno, data) data = self.decipher(self.objid, self.genno, data)
return data return data
## PDF Exceptions ## PDF Exceptions
## ##
class PDFSyntaxError(PDFException): pass class PDFSyntaxError(PDFException): pass
@ -1243,7 +1244,7 @@ class PDFXRef(object):
self.offsets[objid] = (int(genno), int(pos)) self.offsets[objid] = (int(genno), int(pos))
self.load_trailer(parser) self.load_trailer(parser)
return return
KEYWORD_TRAILER = PSKeywordTable.intern('trailer') KEYWORD_TRAILER = PSKeywordTable.intern('trailer')
def load_trailer(self, parser): def load_trailer(self, parser):
try: try:
@ -1284,7 +1285,7 @@ class PDFXRefStream(object):
for first, size in self.index: for first, size in self.index:
for objid in xrange(first, first + size): for objid in xrange(first, first + size):
yield objid yield objid
def load(self, parser, debug=0): def load(self, parser, debug=0):
(_,objid) = parser.nexttoken() # ignored (_,objid) = parser.nexttoken() # ignored
(_,genno) = parser.nexttoken() # ignored (_,genno) = parser.nexttoken() # ignored
@ -1302,7 +1303,7 @@ class PDFXRefStream(object):
self.entlen = self.fl1+self.fl2+self.fl3 self.entlen = self.fl1+self.fl2+self.fl3
self.trailer = stream.dic self.trailer = stream.dic
return return
def getpos(self, objid): def getpos(self, objid):
offset = 0 offset = 0
for first, size in self.index: for first, size in self.index:
@ -1353,7 +1354,7 @@ class PDFDocument(object):
self.parser = parser self.parser = parser
# The document is set to be temporarily ready during collecting # The document is set to be temporarily ready during collecting
# all the basic information about the document, e.g. # all the basic information about the document, e.g.
# the header, the encryption information, and the access rights # the header, the encryption information, and the access rights
# for the document. # for the document.
self.ready = True self.ready = True
# Retrieve the information of each header that was appended # Retrieve the information of each header that was appended
@ -1429,7 +1430,7 @@ class PDFDocument(object):
length = int_value(param.get('Length', 0)) / 8 length = int_value(param.get('Length', 0)) / 8
edcdata = str_value(param.get('EDCData')).decode('base64') edcdata = str_value(param.get('EDCData')).decode('base64')
pdrllic = str_value(param.get('PDRLLic')).decode('base64') pdrllic = str_value(param.get('PDRLLic')).decode('base64')
pdrlpol = str_value(param.get('PDRLPol')).decode('base64') pdrlpol = str_value(param.get('PDRLPol')).decode('base64')
edclist = [] edclist = []
for pair in edcdata.split('\n'): for pair in edcdata.split('\n'):
edclist.append(pair) edclist.append(pair)
@ -1449,9 +1450,9 @@ class PDFDocument(object):
raise ADEPTError('Could not decrypt PDRLPol, aborting ...') raise ADEPTError('Could not decrypt PDRLPol, aborting ...')
else: else:
cutter = -1 * ord(pdrlpol[-1]) cutter = -1 * ord(pdrlpol[-1])
pdrlpol = pdrlpol[:cutter] pdrlpol = pdrlpol[:cutter]
return plaintext[:16] return plaintext[:16]
PASSWORD_PADDING = '(\xbfN^Nu\x8aAd\x00NV\xff\xfa\x01\x08..' \ PASSWORD_PADDING = '(\xbfN^Nu\x8aAd\x00NV\xff\xfa\x01\x08..' \
'\x00\xb6\xd0h>\x80/\x0c\xa9\xfedSiz' '\x00\xb6\xd0h>\x80/\x0c\xa9\xfedSiz'
# experimental aes pw support # experimental aes pw support
@ -1471,14 +1472,14 @@ class PDFDocument(object):
EncMetadata = str_value(param['EncryptMetadata']) EncMetadata = str_value(param['EncryptMetadata'])
except: except:
EncMetadata = 'True' EncMetadata = 'True'
self.is_printable = bool(P & 4) self.is_printable = bool(P & 4)
self.is_modifiable = bool(P & 8) self.is_modifiable = bool(P & 8)
self.is_extractable = bool(P & 16) self.is_extractable = bool(P & 16)
self.is_annotationable = bool(P & 32) self.is_annotationable = bool(P & 32)
self.is_formsenabled = bool(P & 256) self.is_formsenabled = bool(P & 256)
self.is_textextractable = bool(P & 512) self.is_textextractable = bool(P & 512)
self.is_assemblable = bool(P & 1024) self.is_assemblable = bool(P & 1024)
self.is_formprintable = bool(P & 2048) self.is_formprintable = bool(P & 2048)
# Algorithm 3.2 # Algorithm 3.2
password = (password+self.PASSWORD_PADDING)[:32] # 1 password = (password+self.PASSWORD_PADDING)[:32] # 1
hash = hashlib.md5(password) # 2 hash = hashlib.md5(password) # 2
@ -1587,7 +1588,7 @@ class PDFDocument(object):
hash = hashlib.md5(key) hash = hashlib.md5(key)
key = hash.digest()[:min(len(self.decrypt_key) + 5, 16)] key = hash.digest()[:min(len(self.decrypt_key) + 5, 16)]
return key return key
def genkey_v3(self, objid, genno): def genkey_v3(self, objid, genno):
objid = struct.pack('<L', objid ^ 0x3569ac) objid = struct.pack('<L', objid ^ 0x3569ac)
genno = struct.pack('<L', genno ^ 0xca96) genno = struct.pack('<L', genno ^ 0xca96)
@ -1627,14 +1628,14 @@ class PDFDocument(object):
#print cutter #print cutter
plaintext = plaintext[:cutter] plaintext = plaintext[:cutter]
return plaintext return plaintext
def decrypt_rc4(self, objid, genno, data): def decrypt_rc4(self, objid, genno, data):
key = self.genkey(objid, genno) key = self.genkey(objid, genno)
return ARC4.new(key).decrypt(data) return ARC4.new(key).decrypt(data)
KEYWORD_OBJ = PSKeywordTable.intern('obj') KEYWORD_OBJ = PSKeywordTable.intern('obj')
def getobj(self, objid): def getobj(self, objid):
if not self.ready: if not self.ready:
raise PDFException('PDFDocument not initialized') raise PDFException('PDFDocument not initialized')
@ -1704,7 +1705,7 @@ class PDFDocument(object):
## if x: ## if x:
## objid1 = x[-2] ## objid1 = x[-2]
## genno = x[-1] ## genno = x[-1]
## ##
if kwd is not self.KEYWORD_OBJ: if kwd is not self.KEYWORD_OBJ:
raise PDFSyntaxError( raise PDFSyntaxError(
'Invalid object spec: offset=%r' % index) 'Invalid object spec: offset=%r' % index)
@ -1716,7 +1717,7 @@ class PDFDocument(object):
self.objs[objid] = obj self.objs[objid] = obj
return obj return obj
class PDFObjStmRef(object): class PDFObjStmRef(object):
maxindex = 0 maxindex = 0
def __init__(self, objid, stmid, index): def __init__(self, objid, stmid, index):
@ -1726,7 +1727,7 @@ class PDFObjStmRef(object):
if index > PDFObjStmRef.maxindex: if index > PDFObjStmRef.maxindex:
PDFObjStmRef.maxindex = index PDFObjStmRef.maxindex = index
## PDFParser ## PDFParser
## ##
class PDFParser(PSStackParser): class PDFParser(PSStackParser):
@ -1752,7 +1753,7 @@ class PDFParser(PSStackParser):
if token is self.KEYWORD_ENDOBJ: if token is self.KEYWORD_ENDOBJ:
self.add_results(*self.pop(4)) self.add_results(*self.pop(4))
return return
if token is self.KEYWORD_R: if token is self.KEYWORD_R:
# reference to indirect object # reference to indirect object
try: try:
@ -1763,7 +1764,7 @@ class PDFParser(PSStackParser):
except PSSyntaxError: except PSSyntaxError:
pass pass
return return
if token is self.KEYWORD_STREAM: if token is self.KEYWORD_STREAM:
# stream object # stream object
((_,dic),) = self.pop(1) ((_,dic),) = self.pop(1)
@ -1803,7 +1804,7 @@ class PDFParser(PSStackParser):
obj = PDFStream(dic, data, self.doc.decipher) obj = PDFStream(dic, data, self.doc.decipher)
self.push((pos, obj)) self.push((pos, obj))
return return
# others # others
self.push((pos, token)) self.push((pos, token))
return return
@ -1839,7 +1840,7 @@ class PDFParser(PSStackParser):
xref.load(self) xref.load(self)
else: else:
if token is not self.KEYWORD_XREF: if token is not self.KEYWORD_XREF:
raise PDFNoValidXRef('xref not found: pos=%d, token=%r' % raise PDFNoValidXRef('xref not found: pos=%d, token=%r' %
(pos, token)) (pos, token))
self.nextline() self.nextline()
xref = PDFXRef() xref = PDFXRef()
@ -1854,7 +1855,7 @@ class PDFParser(PSStackParser):
pos = int_value(trailer['Prev']) pos = int_value(trailer['Prev'])
self.read_xref_from(pos, xrefs) self.read_xref_from(pos, xrefs)
return return
# read xref tables and trailers # read xref tables and trailers
def read_xref(self): def read_xref(self):
xrefs = [] xrefs = []
@ -1973,7 +1974,7 @@ class PDFSerializer(object):
self.write("%010d 00000 n \n" % xrefs[objid][0]) self.write("%010d 00000 n \n" % xrefs[objid][0])
else: else:
self.write("%010d %05d f \n" % (0, 65535)) self.write("%010d %05d f \n" % (0, 65535))
self.write('trailer\n') self.write('trailer\n')
self.serialize_object(trailer) self.serialize_object(trailer)
self.write('\nstartxref\n%d\n%%%%EOF' % startxref) self.write('\nstartxref\n%d\n%%%%EOF' % startxref)
@ -1993,7 +1994,7 @@ class PDFSerializer(object):
while maxindex >= power: while maxindex >= power:
fl3 += 1 fl3 += 1
power *= 256 power *= 256
index = [] index = []
first = None first = None
prev = None prev = None
@ -2020,14 +2021,14 @@ class PDFSerializer(object):
# we force all generation numbers to be 0 # we force all generation numbers to be 0
# f3 = objref[1] # f3 = objref[1]
f3 = 0 f3 = 0
data.append(struct.pack('>B', f1)) data.append(struct.pack('>B', f1))
data.append(struct.pack('>L', f2)[-fl2:]) data.append(struct.pack('>L', f2)[-fl2:])
data.append(struct.pack('>L', f3)[-fl3:]) data.append(struct.pack('>L', f3)[-fl3:])
index.extend((first, prev - first + 1)) index.extend((first, prev - first + 1))
data = zlib.compress(''.join(data)) data = zlib.compress(''.join(data))
dic = {'Type': LITERAL_XREF, 'Size': prev + 1, 'Index': index, dic = {'Type': LITERAL_XREF, 'Size': prev + 1, 'Index': index,
'W': [1, fl2, fl3], 'Length': len(data), 'W': [1, fl2, fl3], 'Length': len(data),
'Filter': LITERALS_FLATE_DECODE[0], 'Filter': LITERALS_FLATE_DECODE[0],
'Root': trailer['Root'],} 'Root': trailer['Root'],}
if 'Info' in trailer: if 'Info' in trailer:
@ -2049,9 +2050,9 @@ class PDFSerializer(object):
string = string.replace(')', r'\)') string = string.replace(')', r'\)')
# get rid of ciando id # get rid of ciando id
regularexp = re.compile(r'http://www.ciando.com/index.cfm/intRefererID/\d{5}') regularexp = re.compile(r'http://www.ciando.com/index.cfm/intRefererID/\d{5}')
if regularexp.match(string): return ('http://www.ciando.com') if regularexp.match(string): return ('http://www.ciando.com')
return string return string
def serialize_object(self, obj): def serialize_object(self, obj):
if isinstance(obj, dict): if isinstance(obj, dict):
# Correct malformed Mac OS resource forks for Stanza # Correct malformed Mac OS resource forks for Stanza
@ -2075,21 +2076,21 @@ class PDFSerializer(object):
elif isinstance(obj, bool): elif isinstance(obj, bool):
if self.last.isalnum(): if self.last.isalnum():
self.write(' ') self.write(' ')
self.write(str(obj).lower()) self.write(str(obj).lower())
elif isinstance(obj, (int, long, float)): elif isinstance(obj, (int, long, float)):
if self.last.isalnum(): if self.last.isalnum():
self.write(' ') self.write(' ')
self.write(str(obj)) self.write(str(obj))
elif isinstance(obj, PDFObjRef): elif isinstance(obj, PDFObjRef):
if self.last.isalnum(): if self.last.isalnum():
self.write(' ') self.write(' ')
self.write('%d %d R' % (obj.objid, 0)) self.write('%d %d R' % (obj.objid, 0))
elif isinstance(obj, PDFStream): elif isinstance(obj, PDFStream):
### If we don't generate cross ref streams the object streams ### If we don't generate cross ref streams the object streams
### are no longer useful, as we have extracted all objects from ### are no longer useful, as we have extracted all objects from
### them. Therefore leave them out from the output. ### them. Therefore leave them out from the output.
if obj.dic.get('Type') == LITERAL_OBJSTM and not gen_xref_stm: if obj.dic.get('Type') == LITERAL_OBJSTM and not gen_xref_stm:
self.write('(deleted)') self.write('(deleted)')
else: else:
data = obj.get_decdata() data = obj.get_decdata()
self.serialize_object(obj.dic) self.serialize_object(obj.dic)
@ -2101,7 +2102,7 @@ class PDFSerializer(object):
if data[0].isalnum() and self.last.isalnum(): if data[0].isalnum() and self.last.isalnum():
self.write(' ') self.write(' ')
self.write(data) self.write(data)
def serialize_indirect(self, objid, obj): def serialize_indirect(self, objid, obj):
self.write('%d 0 obj' % (objid,)) self.write('%d 0 obj' % (objid,))
self.serialize_object(obj) self.serialize_object(obj)
@ -2136,7 +2137,7 @@ class IneptPDFDeDRM(FileTypePlugin):
Credit given to I <3 Cabbages for the original stand-alone scripts.' Credit given to I <3 Cabbages for the original stand-alone scripts.'
supported_platforms = ['linux', 'osx', 'windows'] supported_platforms = ['linux', 'osx', 'windows']
author = 'DiapDealer' author = 'DiapDealer'
version = (0, 1, 4) version = (0, 1, 5)
minimum_calibre_version = (0, 7, 55) # for the new plugin interface minimum_calibre_version = (0, 7, 55) # for the new plugin interface
file_types = set(['pdf']) file_types = set(['pdf'])
on_import = True on_import = True

View File

@ -385,17 +385,22 @@ def GetIDString():
if isNewInstall(): if isNewInstall():
mungedmac = GetMACAddressMunged() mungedmac = GetMACAddressMunged()
if len(mungedmac) > 7: if len(mungedmac) > 7:
print('Using Munged MAC Address for ID: '+mungedmac)
return mungedmac return mungedmac
sernum = GetVolumeSerialNumber() sernum = GetVolumeSerialNumber()
if len(sernum) > 7: if len(sernum) > 7:
print('Using Volume Serial Number for ID: '+sernum)
return sernum return sernum
diskpart = GetUserHomeAppSupKindleDirParitionName() diskpart = GetUserHomeAppSupKindleDirParitionName()
uuidnum = GetDiskPartitionUUID(diskpart) uuidnum = GetDiskPartitionUUID(diskpart)
if len(uuidnum) > 7: if len(uuidnum) > 7:
print('Using Disk Partition UUID for ID: '+uuidnum)
return uuidnum return uuidnum
mungedmac = GetMACAddressMunged() mungedmac = GetMACAddressMunged()
if len(mungedmac) > 7: if len(mungedmac) > 7:
print('Using Munged MAC Address for ID: '+mungedmac)
return mungedmac return mungedmac
print('Using Fixed constant 9999999999 for ID.')
return '9999999999' return '9999999999'
@ -498,6 +503,7 @@ def getKindleInfoFiles(kInfoFiles):
for resline in reslst: for resline in reslst:
if os.path.isfile(resline): if os.path.isfile(resline):
kInfoFiles.append(resline) kInfoFiles.append(resline)
print('Found K4Mac kindle-info file: ' + resline)
found = True found = True
# add any .rainier*-kinf files # add any .rainier*-kinf files
cmdline = 'find "' + home + '/Library/Application Support" -name ".rainier*-kinf"' cmdline = 'find "' + home + '/Library/Application Support" -name ".rainier*-kinf"'
@ -508,6 +514,7 @@ def getKindleInfoFiles(kInfoFiles):
for resline in reslst: for resline in reslst:
if os.path.isfile(resline): if os.path.isfile(resline):
kInfoFiles.append(resline) kInfoFiles.append(resline)
print('Found k4Mac kinf file: ' + resline)
found = True found = True
# add any .kinf2011 files # add any .kinf2011 files
cmdline = 'find "' + home + '/Library/Application Support" -name ".kinf2011"' cmdline = 'find "' + home + '/Library/Application Support" -name ".kinf2011"'
@ -518,9 +525,10 @@ def getKindleInfoFiles(kInfoFiles):
for resline in reslst: for resline in reslst:
if os.path.isfile(resline): if os.path.isfile(resline):
kInfoFiles.append(resline) kInfoFiles.append(resline)
print('Found k4Mac kinf2011 file: ' + resline)
found = True found = True
if not found: if not found:
print('No kindle-info files have been found.') print('No k4Mac kindle-info/kinf/kinf2011 files have been found.')
return kInfoFiles return kInfoFiles
# determine type of kindle info provided and return a # determine type of kindle info provided and return a

View File

@ -151,7 +151,9 @@ def GetVolumeSerialNumber():
GetVolumeSerialNumber = GetVolumeSerialNumber() GetVolumeSerialNumber = GetVolumeSerialNumber()
def GetIDString(): def GetIDString():
return GetVolumeSerialNumber() vsn = GetVolumeSerialNumber()
print('Using Volume Serial Number for ID: '+vsn)
return vsn
def getLastError(): def getLastError():
GetLastError = kernel32.GetLastError GetLastError = kernel32.GetLastError
@ -210,37 +212,40 @@ def getKindleInfoFiles(kInfoFiles):
if 'LOCALAPPDATA' in os.environ.keys(): if 'LOCALAPPDATA' in os.environ.keys():
path = os.environ['LOCALAPPDATA'] path = os.environ['LOCALAPPDATA']
print "searching for kinfoFiles in ", path print('searching for kinfoFiles in ' + path)
found = False
# first look for older kindle-info files # first look for older kindle-info files
kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info' kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info'
if not os.path.isfile(kinfopath): if os.path.isfile(kinfopath):
print('No kindle.info files have not been found.') found = True
else: print('Found K4PC kindle.info file: ' + kinfopath)
kInfoFiles.append(kinfopath) kInfoFiles.append(kinfopath)
# now look for newer (K4PC 1.5.0 and later rainier.2.1.1.kinf file # now look for newer (K4PC 1.5.0 and later rainier.2.1.1.kinf file
kinfopath = path +'\\Amazon\\Kindle For PC\\storage\\rainier.2.1.1.kinf' kinfopath = path +'\\Amazon\\Kindle For PC\\storage\\rainier.2.1.1.kinf'
if not os.path.isfile(kinfopath): if os.path.isfile(kinfopath):
print('No K4PC 1.5.X .kinf files have not been found.') found = True
else: print('Found K4PC 1.5.X kinf file: ' + kinfopath)
kInfoFiles.append(kinfopath) kInfoFiles.append(kinfopath)
# now look for even newer (K4PC 1.6.0 and later) rainier.2.1.1.kinf file # now look for even newer (K4PC 1.6.0 and later) rainier.2.1.1.kinf file
kinfopath = path +'\\Amazon\\Kindle\\storage\\rainier.2.1.1.kinf' kinfopath = path +'\\Amazon\\Kindle\\storage\\rainier.2.1.1.kinf'
if not os.path.isfile(kinfopath): if os.path.isfile(kinfopath):
print('No K4PC 1.6.X .kinf files have not been found.') found = True
else: print('Found K4PC 1.6.X kinf file: ' + kinfopath)
kInfoFiles.append(kinfopath) kInfoFiles.append(kinfopath)
# now look for even newer (K4PC 1.9.0 and later) .kinf2011 file # now look for even newer (K4PC 1.9.0 and later) .kinf2011 file
kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2011' kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2011'
if not os.path.isfile(kinfopath): if os.path.isfile(kinfopath):
print('No K4PC 1.9.X .kinf files have not been found.') found = True
else: print('Found K4PC kinf2011 file: ' + kinfopath)
kInfoFiles.append(kinfopath) kInfoFiles.append(kinfopath)
if not found:
print('No K4PC kindle.info/kinf/kinf2011 files have been found.')
return kInfoFiles return kInfoFiles

View File

@ -57,8 +57,11 @@
# 0.33 - Performance improvements for large files (concatenation) # 0.33 - Performance improvements for large files (concatenation)
# 0.34 - Performance improvements in decryption (libalfcrypto) # 0.34 - Performance improvements in decryption (libalfcrypto)
# 0.35 - add interface to get mobi_version # 0.35 - add interface to get mobi_version
# 0.36 - fixed problem with TEXtREAd and getBookTitle interface
# 0.37 - Fixed double announcement for stand-alone operation
__version__ = '0.35'
__version__ = '0.37'
import sys import sys
@ -168,9 +171,10 @@ class MobiBook:
off = self.sections[section][0] off = self.sections[section][0]
return self.data_file[off:endoff] return self.data_file[off:endoff]
def __init__(self, infile): def __init__(self, infile, announce = True):
print ('MobiDeDrm v%(__version__)s. ' if announce:
'Copyright 2008-2011 The Dark Reverser et al.' % globals()) print ('MobiDeDrm v%(__version__)s. '
'Copyright 2008-2012 The Dark Reverser et al.' % globals())
# initial sanity check on file # initial sanity check on file
self.data_file = file(infile, 'rb').read() self.data_file = file(infile, 'rb').read()
@ -198,6 +202,7 @@ class MobiBook:
print "Book has format: ", self.magic print "Book has format: ", self.magic
self.extra_data_flags = 0 self.extra_data_flags = 0
self.mobi_length = 0 self.mobi_length = 0
self.mobi_codepage = 1252
self.mobi_version = -1 self.mobi_version = -1
self.meta_array = {} self.meta_array = {}
return return
@ -248,18 +253,19 @@ class MobiBook:
65001 : 'utf-8', 65001 : 'utf-8',
} }
title = '' title = ''
if 503 in self.meta_array: codec = 'windows-1252'
title = self.meta_array[503] if self.magic == 'BOOKMOBI':
else : if 503 in self.meta_array:
toff, tlen = struct.unpack('>II', self.sect[0x54:0x5c]) title = self.meta_array[503]
tend = toff + tlen else:
title = self.sect[toff:tend] toff, tlen = struct.unpack('>II', self.sect[0x54:0x5c])
tend = toff + tlen
title = self.sect[toff:tend]
if self.mobi_codepage in codec_map.keys():
codec = codec_map[self.mobi_codepage]
if title == '': if title == '':
title = self.header[:32] title = self.header[:32]
title = title.split("\0")[0] title = title.split("\0")[0]
codec = 'windows-1252'
if self.mobi_codepage in codec_map.keys():
codec = codec_map[self.mobi_codepage]
return unicode(title, codec).encode('utf-8') return unicode(title, codec).encode('utf-8')
def getPIDMetaInfo(self): def getPIDMetaInfo(self):
@ -375,7 +381,7 @@ class MobiBook:
raise DrmException("Not yet initialised with PID. Must be opened with Mobipocket Reader first.") raise DrmException("Not yet initialised with PID. Must be opened with Mobipocket Reader first.")
found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids) found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids)
if not found_key: if not found_key:
raise DrmException("No key found. Please report this failure for help.") raise DrmException("No key found in " + str(len(goodpids)) + " keys tried. Please report this failure for help.")
# kill the drm keys # kill the drm keys
self.patchSection(0, "\0" * drm_size, drm_ptr) self.patchSection(0, "\0" * drm_size, drm_ptr)
# kill the drm pointers # kill the drm pointers
@ -411,26 +417,26 @@ class MobiBook:
print "done" print "done"
return return
def getUnencryptedBook(infile,pid): def getUnencryptedBook(infile,pid,announce=True):
if not os.path.isfile(infile): if not os.path.isfile(infile):
raise DrmException('Input File Not Found') raise DrmException('Input File Not Found')
book = MobiBook(infile) book = MobiBook(infile,announce)
book.processBook([pid]) book.processBook([pid])
return book.mobi_data return book.mobi_data
def getUnencryptedBookWithList(infile,pidlist): def getUnencryptedBookWithList(infile,pidlist,announce=True):
if not os.path.isfile(infile): if not os.path.isfile(infile):
raise DrmException('Input File Not Found') raise DrmException('Input File Not Found')
book = MobiBook(infile) book = MobiBook(infile, announce)
book.processBook(pidlist) book.processBook(pidlist)
return book.mobi_data return book.mobi_data
def main(argv=sys.argv): def main(argv=sys.argv):
print ('MobiDeDrm v%(__version__)s. ' print ('MobiDeDrm v%(__version__)s. '
'Copyright 2008-2011 The Dark Reverser et al.' % globals()) 'Copyright 2008-2012 The Dark Reverser et al.' % globals())
if len(argv)<3 or len(argv)>4: if len(argv)<3 or len(argv)>4:
print "Removes protection from Kindle/Mobipocket and Kindle/Print Replica ebooks" print "Removes protection from Kindle/Mobipocket, Kindle/KF8 and Kindle/Print Replica ebooks"
print "Usage:" print "Usage:"
print " %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0] print " %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0]
return 1 return 1
@ -442,7 +448,7 @@ def main(argv=sys.argv):
else: else:
pidlist = {} pidlist = {}
try: try:
stripped_file = getUnencryptedBookWithList(infile, pidlist) stripped_file = getUnencryptedBookWithList(infile, pidlist, False)
file(outfile, 'wb').write(stripped_file) file(outfile, 'wb').write(stripped_file)
except DrmException, e: except DrmException, e:
print "Error: %s" % e print "Error: %s" % e

View File

@ -1,49 +1,46 @@
{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf360 {\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf360
{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\fonttbl\f0\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;} {\colortbl;\red255\green255\blue255;}
\paperw11900\paperh16840\margl1440\margr1440\vieww10320\viewh9840\viewkind0 \paperw11900\paperh16840\margl1440\margr1440\vieww12360\viewh16560\viewkind0
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\ql\qnatural\pardirnatural \pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\qc\pardirnatural
\f0\b\fs24 \cf0 ReadMe_DeDRM_X.X \f0\b\fs24 \cf0 DeDRM ReadMe
\b0 \ \b0 \
\
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\qj\pardirnatural
\cf0 DeDRM_X.X is an AppleScript droplet that allows users to drag and drop ebooks or folders of ebooks onto the DeDRM droplet to have the DRM removed. It repackages the all the "tools" DeDRM python software in one easy to use program that remembers preferences and settings.\
\
It should work without manual configuration with Kindle for Mac ebooks and Adobe Adept epub and pdf ebooks.\
\
To remove the DRM from standalone Kindle ebooks, eReader pdb ebooks, Barnes and Noble epubs, and Mobipocket ebooks requires the user to double-click the DeDRM droplet and set some additional Preferences including:\
\
Mobipocket, Kindle for iPhone/iPad/iPodTouch: 10 digit PID\
Kindle (not Kindle Fire): 16 digit Serial Number\
Barnes & Noble key files (bnepubkey.b64)\
eReader Social DRM: (Name:Last 8 digits of CC number)\
Additional Above Adept key files (.der)\
Location for DRM-free ebooks.\
\
Once these preferences have been set, the user can simply drag and drop ebooks onto the DeDRM droplet to remove the DRM.\
\
This program requires Mac OS X 10.5, 10.5 or 10.7 (Leopard, Snow Leopard or Lion)\
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\ql\qnatural\pardirnatural \pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\ql\qnatural\pardirnatural
\cf0 \ \cf0 \
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\qj\pardirnatural
\cf0 DeDRM is an application that packs all of the python drm-removal software into one easy to use program that remembers preferences and settings.\
It works without manual configuration with Kindle for Mac ebooks and Adobe Adept ePub and PDF ebooks.\
\
To remove the DRM of Kindle ebooks from eInk Kindles, eReader pdb ebooks, Barnes and Noble ePubs, or Mobipocket ebooks, you must first run DeDRM application (by double-clicking it) and set some additional Preferences including:\
\
Kindle (not Kindle Fire): 16 digit Serial Number\
Barnes & Noble ePub: Name and CC number or key file (bnepubkey.b64)\
eReader Social DRM: Name and last 8 digits of CC number\
Mobipocket: 10 digit PID\
\
A final preference is the destination folder for the DRM-free copies of your ebooks that the application produces. This can be either the same folder as the original ebook, or a folder of your choice.\
\
Once these preferences have been set, you can drag and drop ebooks (or folders of ebooks) onto the DeDRM droplet to remove the DRM.\
\
This program requires Mac OS X 10.5 or above. \
\ \
\ \
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\ql\qnatural\pardirnatural
\b Installation\ \b \cf0 Installation
\b0 \ \b0 \
1. From tools_vX.X\\DeDRM_Applications\\, double click on DeDRM_X.X.zip to extract its contents. \ Drag the DeDRM application from from tools_vX.X\\DeDRM_Applications\\Macintosh (the location of this ReadMe) to your Applications folder, or anywhere else you find convenient.\
\
2. Move the resulting DeDRM X.X.app AppleScript droplet to wherever you keep you other applications. (Typically your Applications folder.)\
\
3. Optionally drag it into your dock, to make it easily available.\
\
\ \
\ \
\b Use\ \b Use
\b0 \ \b0 \
1. To set the preferences simply double-click the Applescript droplet in your Applications folder or click on its icon in your dock, and follow the instructions in the dialogs.\ 1. To set the preferences, double-click the application and follow the instructions in the dialogs.\
2. Drag & Drop DRMed ebooks or folders of DRMed ebooks onto the application icon when it is not running.\
\ \
2. Drag & Drop DRMed ebooks or folders containing DRMed ebooks onto the Application, either in your Applications folder, or the icon in your dock.} \
\b Troubleshooting\
\b0 A log is created on your desktop containing detailed information from all the scripts. If you have any problems decrypting your ebooks, quote the contents of this log in a comment at Apprentice Alf's blog.}

View File

@ -24,17 +24,17 @@
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>droplet</string> <string>droplet</string>
<key>CFBundleGetInfoString</key> <key>CFBundleGetInfoString</key>
<string>DeDRM 5.1, Written 20102012 by Apprentice Alf and others.</string> <string>DeDRM 5.2, Written 20102012 by Apprentice Alf and others.</string>
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
<string>droplet</string> <string>DeDRM</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>DeDRM 5.1</string> <string>DeDRM 5.2</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>5.1</string> <string>5.2</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>dplt</string> <string>dplt</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
@ -43,16 +43,12 @@
<true/> <true/>
<key>WindowState</key> <key>WindowState</key>
<dict> <dict>
<key>dividerCollapsed</key>
<false/>
<key>eventLogLevel</key>
<integer>-1</integer>
<key>name</key> <key>name</key>
<string>ScriptWindowState</string> <string>ScriptWindowState</string>
<key>positionOfDivider</key> <key>positionOfDivider</key>
<real>460</real> <real>554</real>
<key>savedFrame</key> <key>savedFrame</key>
<string>1518 90 1316 746 1440 -150 1680 1050 </string> <string>42 60 922 818 0 0 1440 878 </string>
<key>selectedTabView</key> <key>selectedTabView</key>
<string>event log</string> <string>event log</string>
</dict> </dict>

View File

@ -2,10 +2,16 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>BuildMachineOSBuild</key>
<string>10K549</string>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>English</string> <string>English</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>DeDRM Progress</string> <string>DeDRM Progress</string>
<key>CFBundleGetInfoString</key>
<string>DeDRM Progress 1.1, Written 2010, 2012 by Apprentice Alf and others.</string>
<key>CFBundleIconFile</key>
<string>DeDRM Progress</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>com.apprenticealf.DeDRMProgress</string> <string>com.apprenticealf.DeDRMProgress</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
@ -15,11 +21,25 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.0</string> <string>1.1</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.0</string> <string>1.0</string>
<key>DTCompiler</key>
<string></string>
<key>DTPlatformBuild</key>
<string>10M2518</string>
<key>DTPlatformVersion</key>
<string>PG</string>
<key>DTSDKBuild</key>
<string>9L31a</string>
<key>DTSDKName</key>
<string>macosx10.5</string>
<key>DTXcode</key>
<string>0400</string>
<key>DTXcodeBuild</key>
<string>10M2518</string>
<key>NSAppleScriptEnabled</key> <key>NSAppleScriptEnabled</key>
<string>YES</string> <string>YES</string>
<key>NSMainNibFile</key> <key>NSMainNibFile</key>

View File

@ -0,0 +1,899 @@
#! /usr/bin/python
"""
Comprehensive Mazama Book DRM with Topaz Cryptography V2.2
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdBHJ4CNc6DNFCw4MRCw4SWAK6
M8hYfnNEI0yQmn5Ti+W8biT7EatpauE/5jgQMPBmdNrDr1hbHyHBSP7xeC2qlRWC
B62UCxeu/fpfnvNHDN/wPWWH4jynZ2M6cdcnE5LQ+FfeKqZn7gnG2No1U9h7oOHx
y2/pHuYme7U1TsgSjwIDAQAB
-----END PUBLIC KEY-----
"""
from __future__ import with_statement
import csv
import sys
import os
import getopt
import zlib
from struct import pack
from struct import unpack
from ctypes import windll, c_char_p, c_wchar_p, c_uint, POINTER, byref, \
create_unicode_buffer, create_string_buffer, CFUNCTYPE, addressof, \
string_at, Structure, c_void_p, cast
import _winreg as winreg
import Tkinter
import Tkconstants
import tkMessageBox
import traceback
import hashlib
MAX_PATH = 255
kernel32 = windll.kernel32
advapi32 = windll.advapi32
crypt32 = windll.crypt32
global kindleDatabase
global bookFile
global bookPayloadOffset
global bookHeaderRecords
global bookMetadata
global bookKey
global command
#
# Various character maps used to decrypt books. Probably supposed to act as obfuscation
#
charMap1 = "n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M"
charMap2 = "AaZzB0bYyCc1XxDdW2wEeVv3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_"
charMap3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
charMap4 = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
#
# Exceptions for all the problems that might happen during the script
#
class CMBDTCError(Exception):
pass
class CMBDTCFatal(Exception):
pass
#
# Stolen stuff
#
class DataBlob(Structure):
_fields_ = [('cbData', c_uint),
('pbData', c_void_p)]
DataBlob_p = POINTER(DataBlob)
def GetSystemDirectory():
GetSystemDirectoryW = kernel32.GetSystemDirectoryW
GetSystemDirectoryW.argtypes = [c_wchar_p, c_uint]
GetSystemDirectoryW.restype = c_uint
def GetSystemDirectory():
buffer = create_unicode_buffer(MAX_PATH + 1)
GetSystemDirectoryW(buffer, len(buffer))
return buffer.value
return GetSystemDirectory
GetSystemDirectory = GetSystemDirectory()
def GetVolumeSerialNumber():
GetVolumeInformationW = kernel32.GetVolumeInformationW
GetVolumeInformationW.argtypes = [c_wchar_p, c_wchar_p, c_uint,
POINTER(c_uint), POINTER(c_uint),
POINTER(c_uint), c_wchar_p, c_uint]
GetVolumeInformationW.restype = c_uint
def GetVolumeSerialNumber(path):
vsn = c_uint(0)
GetVolumeInformationW(path, None, 0, byref(vsn), None, None, None, 0)
return vsn.value
return GetVolumeSerialNumber
GetVolumeSerialNumber = GetVolumeSerialNumber()
def GetUserName():
GetUserNameW = advapi32.GetUserNameW
GetUserNameW.argtypes = [c_wchar_p, POINTER(c_uint)]
GetUserNameW.restype = c_uint
def GetUserName():
buffer = create_unicode_buffer(32)
size = c_uint(len(buffer))
while not GetUserNameW(buffer, byref(size)):
buffer = create_unicode_buffer(len(buffer) * 2)
size.value = len(buffer)
return buffer.value.encode('utf-16-le')[::2]
return GetUserName
GetUserName = GetUserName()
def CryptUnprotectData():
_CryptUnprotectData = crypt32.CryptUnprotectData
_CryptUnprotectData.argtypes = [DataBlob_p, c_wchar_p, DataBlob_p,
c_void_p, c_void_p, c_uint, DataBlob_p]
_CryptUnprotectData.restype = c_uint
def CryptUnprotectData(indata, entropy):
indatab = create_string_buffer(indata)
indata = DataBlob(len(indata), cast(indatab, c_void_p))
entropyb = create_string_buffer(entropy)
entropy = DataBlob(len(entropy), cast(entropyb, c_void_p))
outdata = DataBlob()
if not _CryptUnprotectData(byref(indata), None, byref(entropy),
None, None, 0, byref(outdata)):
raise CMBDTCFatal("Failed to Unprotect Data")
return string_at(outdata.pbData, outdata.cbData)
return CryptUnprotectData
CryptUnprotectData = CryptUnprotectData()
#
# Returns the MD5 digest of "message"
#
def MD5(message):
ctx = hashlib.md5()
ctx.update(message)
return ctx.digest()
#
# Returns the MD5 digest of "message"
#
def SHA1(message):
ctx = hashlib.sha1()
ctx.update(message)
return ctx.digest()
#
# Open the book file at path
#
def openBook(path):
try:
return open(path,'rb')
except:
raise CMBDTCFatal("Could not open book file: " + path)
#
# Encode the bytes in data with the characters in map
#
def encode(data, map):
result = ""
for char in data:
value = ord(char)
Q = (value ^ 0x80) // len(map)
R = value % len(map)
result += map[Q]
result += map[R]
return result
#
# Hash the bytes in data and then encode the digest with the characters in map
#
def encodeHash(data,map):
return encode(MD5(data),map)
#
# Decode the string in data with the characters in map. Returns the decoded bytes
#
def decode(data,map):
result = ""
for i in range (0,len(data),2):
high = map.find(data[i])
low = map.find(data[i+1])
value = (((high * 0x40) ^ 0x80) & 0xFF) + low
result += pack("B",value)
return result
#
# Locate and open the Kindle.info file (Hopefully in the way it is done in the Kindle application)
#
def openKindleInfo():
regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\")
path = winreg.QueryValueEx(regkey, 'Local AppData')[0]
return open(path+'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info','r')
#
# Parse the Kindle.info file and return the records as a list of key-values
#
def parseKindleInfo():
DB = {}
infoReader = openKindleInfo()
infoReader.read(1)
data = infoReader.read()
items = data.split('{')
for item in items:
splito = item.split(':')
DB[splito[0]] =splito[1]
return DB
#
# Find if the original string for a hashed/encoded string is known. If so return the original string othwise return an empty string. (Totally not optimal)
#
def findNameForHash(hash):
names = ["kindle.account.tokens","kindle.cookie.item","eulaVersionAccepted","login_date","kindle.token.item","login","kindle.key.item","kindle.name.info","kindle.device.info", "MazamaRandomNumber"]
result = ""
for name in names:
if hash == encodeHash(name, charMap2):
result = name
break
return name
#
# Print all the records from the kindle.info file (option -i)
#
def printKindleInfo():
for record in kindleDatabase:
name = findNameForHash(record)
if name != "" :
print (name)
print ("--------------------------\n")
else :
print ("Unknown Record")
print getKindleInfoValueForHash(record)
print "\n"
#
# Get a record from the Kindle.info file for the key "hashedKey" (already hashed and encoded). Return the decoded and decrypted record
#
def getKindleInfoValueForHash(hashedKey):
global kindleDatabase
encryptedValue = decode(kindleDatabase[hashedKey],charMap2)
return CryptUnprotectData(encryptedValue,"")
#
# Get a record from the Kindle.info file for the string in "key" (plaintext). Return the decoded and decrypted record
#
def getKindleInfoValueForKey(key):
return getKindleInfoValueForHash(encodeHash(key,charMap2))
#
# Get a 7 bit encoded number from the book file
#
def bookReadEncodedNumber():
flag = False
data = ord(bookFile.read(1))
if data == 0xFF:
flag = True
data = ord(bookFile.read(1))
if data >= 0x80:
datax = (data & 0x7F)
while data >= 0x80 :
data = ord(bookFile.read(1))
datax = (datax <<7) + (data & 0x7F)
data = datax
if flag:
data = -data
return data
#
# Encode a number in 7 bit format
#
def encodeNumber(number):
result = ""
negative = False
flag = 0
if number < 0 :
number = -number + 1
negative = True
while True:
byte = number & 0x7F
number = number >> 7
byte += flag
result += chr(byte)
flag = 0x80
if number == 0 :
if (byte == 0xFF and negative == False) :
result += chr(0x80)
break
if negative:
result += chr(0xFF)
return result[::-1]
#
# Get a length prefixed string from the file
#
def bookReadString():
stringLength = bookReadEncodedNumber()
return unpack(str(stringLength)+"s",bookFile.read(stringLength))[0]
#
# Returns a length prefixed string
#
def lengthPrefixString(data):
return encodeNumber(len(data))+data
#
# Read and return the data of one header record at the current book file position [[offset,compressedLength,decompressedLength],...]
#
def bookReadHeaderRecordData():
nbValues = bookReadEncodedNumber()
values = []
for i in range (0,nbValues):
values.append([bookReadEncodedNumber(),bookReadEncodedNumber(),bookReadEncodedNumber()])
return values
#
# Read and parse one header record at the current book file position and return the associated data [[offset,compressedLength,decompressedLength],...]
#
def parseTopazHeaderRecord():
if ord(bookFile.read(1)) != 0x63:
raise CMBDTCFatal("Parse Error : Invalid Header")
tag = bookReadString()
record = bookReadHeaderRecordData()
return [tag,record]
#
# Parse the header of a Topaz file, get all the header records and the offset for the payload
#
def parseTopazHeader():
global bookHeaderRecords
global bookPayloadOffset
magic = unpack("4s",bookFile.read(4))[0]
if magic != 'TPZ0':
raise CMBDTCFatal("Parse Error : Invalid Header, not a Topaz file")
nbRecords = bookReadEncodedNumber()
bookHeaderRecords = {}
for i in range (0,nbRecords):
result = parseTopazHeaderRecord()
bookHeaderRecords[result[0]] = result[1]
if ord(bookFile.read(1)) != 0x64 :
raise CMBDTCFatal("Parse Error : Invalid Header")
bookPayloadOffset = bookFile.tell()
#
# Get a record in the book payload, given its name and index. If necessary the record is decrypted. The record is not decompressed
#
def getBookPayloadRecord(name, index):
encrypted = False
try:
recordOffset = bookHeaderRecords[name][index][0]
except:
raise CMBDTCFatal("Parse Error : Invalid Record, record not found")
bookFile.seek(bookPayloadOffset + recordOffset)
tag = bookReadString()
if tag != name :
raise CMBDTCFatal("Parse Error : Invalid Record, record name doesn't match")
recordIndex = bookReadEncodedNumber()
if recordIndex < 0 :
encrypted = True
recordIndex = -recordIndex -1
if recordIndex != index :
raise CMBDTCFatal("Parse Error : Invalid Record, index doesn't match")
if bookHeaderRecords[name][index][2] != 0 :
record = bookFile.read(bookHeaderRecords[name][index][2])
else:
record = bookFile.read(bookHeaderRecords[name][index][1])
if encrypted:
ctx = topazCryptoInit(bookKey)
record = topazCryptoDecrypt(record,ctx)
return record
#
# Extract, decrypt and decompress a book record indicated by name and index and print it or save it in "filename"
#
def extractBookPayloadRecord(name, index, filename):
compressed = False
try:
compressed = bookHeaderRecords[name][index][2] != 0
record = getBookPayloadRecord(name,index)
except:
print("Could not find record")
if compressed:
try:
record = zlib.decompress(record)
except:
raise CMBDTCFatal("Could not decompress record")
if filename != "":
try:
file = open(filename,"wb")
file.write(record)
file.close()
except:
raise CMBDTCFatal("Could not write to destination file")
else:
print(record)
#
# return next record [key,value] from the book metadata from the current book position
#
def readMetadataRecord():
return [bookReadString(),bookReadString()]
#
# Parse the metadata record from the book payload and return a list of [key,values]
#
def parseMetadata():
global bookHeaderRecords
global bookPayloadAddress
global bookMetadata
bookMetadata = {}
bookFile.seek(bookPayloadOffset + bookHeaderRecords["metadata"][0][0])
tag = bookReadString()
if tag != "metadata" :
raise CMBDTCFatal("Parse Error : Record Names Don't Match")
flags = ord(bookFile.read(1))
nbRecords = ord(bookFile.read(1))
for i in range (0,nbRecords) :
record =readMetadataRecord()
bookMetadata[record[0]] = record[1]
#
# Returns two bit at offset from a bit field
#
def getTwoBitsFromBitField(bitField,offset):
byteNumber = offset // 4
bitPosition = 6 - 2*(offset % 4)
return ord(bitField[byteNumber]) >> bitPosition & 3
#
# Returns the six bits at offset from a bit field
#
def getSixBitsFromBitField(bitField,offset):
offset *= 3
value = (getTwoBitsFromBitField(bitField,offset) <<4) + (getTwoBitsFromBitField(bitField,offset+1) << 2) +getTwoBitsFromBitField(bitField,offset+2)
return value
#
# 8 bits to six bits encoding from hash to generate PID string
#
def encodePID(hash):
global charMap3
PID = ""
for position in range (0,8):
PID += charMap3[getSixBitsFromBitField(hash,position)]
return PID
#
# Context initialisation for the Topaz Crypto
#
def topazCryptoInit(key):
ctx1 = 0x0CAFFE19E
for keyChar in key:
keyByte = ord(keyChar)
ctx2 = ctx1
ctx1 = ((((ctx1 >>2) * (ctx1 >>7))&0xFFFFFFFF) ^ (keyByte * keyByte * 0x0F902007)& 0xFFFFFFFF )
return [ctx1,ctx2]
#
# decrypt data with the context prepared by topazCryptoInit()
#
def topazCryptoDecrypt(data, ctx):
ctx1 = ctx[0]
ctx2 = ctx[1]
plainText = ""
for dataChar in data:
dataByte = ord(dataChar)
m = (dataByte ^ ((ctx1 >> 3) &0xFF) ^ ((ctx2<<3) & 0xFF)) &0xFF
ctx2 = ctx1
ctx1 = (((ctx1 >> 2) * (ctx1 >> 7)) &0xFFFFFFFF) ^((m * m * 0x0F902007) &0xFFFFFFFF)
plainText += chr(m)
return plainText
#
# Decrypt a payload record with the PID
#
def decryptRecord(data,PID):
ctx = topazCryptoInit(PID)
return topazCryptoDecrypt(data, ctx)
#
# Try to decrypt a dkey record (contains the book PID)
#
def decryptDkeyRecord(data,PID):
record = decryptRecord(data,PID)
fields = unpack("3sB8sB8s3s",record)
if fields[0] != "PID" or fields[5] != "pid" :
raise CMBDTCError("Didn't find PID magic numbers in record")
elif fields[1] != 8 or fields[3] != 8 :
raise CMBDTCError("Record didn't contain correct length fields")
elif fields[2] != PID :
raise CMBDTCError("Record didn't contain PID")
return fields[4]
#
# Decrypt all the book's dkey records (contain the book PID)
#
def decryptDkeyRecords(data,PID):
nbKeyRecords = ord(data[0])
records = []
data = data[1:]
for i in range (0,nbKeyRecords):
length = ord(data[0])
try:
key = decryptDkeyRecord(data[1:length+1],PID)
records.append(key)
except CMBDTCError:
pass
data = data[1+length:]
return records
#
# Encryption table used to generate the device PID
#
def generatePidEncryptionTable() :
table = []
for counter1 in range (0,0x100):
value = counter1
for counter2 in range (0,8):
if (value & 1 == 0) :
value = value >> 1
else :
value = value >> 1
value = value ^ 0xEDB88320
table.append(value)
return table
#
# Seed value used to generate the device PID
#
def generatePidSeed(table,dsn) :
value = 0
for counter in range (0,4) :
index = (ord(dsn[counter]) ^ value) &0xFF
value = (value >> 8) ^ table[index]
return value
#
# Generate the device PID
#
def generateDevicePID(table,dsn,nbRoll):
seed = generatePidSeed(table,dsn)
pidAscii = ""
pid = [(seed >>24) &0xFF,(seed >> 16) &0xff,(seed >> 8) &0xFF,(seed) & 0xFF,(seed>>24) & 0xFF,(seed >> 16) &0xff,(seed >> 8) &0xFF,(seed) & 0xFF]
index = 0
for counter in range (0,nbRoll):
pid[index] = pid[index] ^ ord(dsn[counter])
index = (index+1) %8
for counter in range (0,8):
index = ((((pid[counter] >>5) & 3) ^ pid[counter]) & 0x1f) + (pid[counter] >> 7)
pidAscii += charMap4[index]
return pidAscii
#
# Create decrypted book payload
#
def createDecryptedPayload(payload):
# store data to be able to create the header later
headerData= []
currentOffset = 0
# Add social DRM to decrypted files
try:
data = getKindleInfoValueForKey("kindle.name.info")+":"+ getKindleInfoValueForKey("login")
if payload!= None:
payload.write(lengthPrefixString("sdrm"))
payload.write(encodeNumber(0))
payload.write(data)
else:
currentOffset += len(lengthPrefixString("sdrm"))
currentOffset += len(encodeNumber(0))
currentOffset += len(data)
except:
pass
for headerRecord in bookHeaderRecords:
name = headerRecord
newRecord = []
if name != "dkey" :
for index in range (0,len(bookHeaderRecords[name])) :
offset = currentOffset
if payload != None:
# write tag
payload.write(lengthPrefixString(name))
# write data
payload.write(encodeNumber(index))
payload.write(getBookPayloadRecord(name, index))
else :
currentOffset += len(lengthPrefixString(name))
currentOffset += len(encodeNumber(index))
currentOffset += len(getBookPayloadRecord(name, index))
newRecord.append([offset,bookHeaderRecords[name][index][1],bookHeaderRecords[name][index][2]])
headerData.append([name,newRecord])
return headerData
#
# Create decrypted book
#
def createDecryptedBook(outputFile):
outputFile = open(outputFile,"wb")
# Write the payload in a temporary file
headerData = createDecryptedPayload(None)
outputFile.write("TPZ0")
outputFile.write(encodeNumber(len(headerData)))
for header in headerData :
outputFile.write(chr(0x63))
outputFile.write(lengthPrefixString(header[0]))
outputFile.write(encodeNumber(len(header[1])))
for numbers in header[1] :
outputFile.write(encodeNumber(numbers[0]))
outputFile.write(encodeNumber(numbers[1]))
outputFile.write(encodeNumber(numbers[2]))
outputFile.write(chr(0x64))
createDecryptedPayload(outputFile)
outputFile.close()
#
# Set the command to execute by the programm according to cmdLine parameters
#
def setCommand(name) :
global command
if command != "" :
raise CMBDTCFatal("Invalid command line parameters")
else :
command = name
#
# Program usage
#
def usage():
print("\nUsage:")
print("\nCMBDTC.py [options] bookFileName\n")
print("-p Adds a PID to the list of PIDs that are tried to decrypt the book key (can be used several times)")
print("-d Saves a decrypted copy of the book")
print("-r Prints or writes to disk a record indicated in the form name:index (e.g \"img:0\")")
print("-o Output file name to write records and decrypted books")
print("-v Verbose (can be used several times)")
print("-i Prints kindle.info database")
#
# Main
#
def main(argv=sys.argv):
global kindleDatabase
global bookMetadata
global bookKey
global bookFile
global command
progname = os.path.basename(argv[0])
verbose = 0
recordName = ""
recordIndex = 0
outputFile = ""
PIDs = []
kindleDatabase = None
command = ""
try:
opts, args = getopt.getopt(sys.argv[1:], "vdir:o:p:")
except getopt.GetoptError, err:
# print help information and exit:
print str(err) # will print something like "option -a not recognized"
usage()
sys.exit(2)
if len(opts) == 0 and len(args) == 0 :
usage()
sys.exit(2)
for o, a in opts:
if o == "-v":
verbose+=1
if o == "-i":
setCommand("printInfo")
if o =="-o":
if a == None :
raise CMBDTCFatal("Invalid parameter for -o")
outputFile = a
if o =="-r":
setCommand("printRecord")
try:
recordName,recordIndex = a.split(':')
except:
raise CMBDTCFatal("Invalid parameter for -r")
if o =="-p":
PIDs.append(a)
if o =="-d":
setCommand("doit")
if command == "" :
raise CMBDTCFatal("No action supplied on command line")
#
# Read the encrypted database
#
try:
kindleDatabase = parseKindleInfo()
except Exception, message:
if verbose>0:
print(message)
if kindleDatabase != None :
if command == "printInfo" :
printKindleInfo()
#
# Compute the DSN
#
# Get the Mazama Random number
MazamaRandomNumber = getKindleInfoValueForKey("MazamaRandomNumber")
# Get the HDD serial
encodedSystemVolumeSerialNumber = encodeHash(str(GetVolumeSerialNumber(GetSystemDirectory().split('\\')[0] + '\\')),charMap1)
# Get the current user name
encodedUsername = encodeHash(GetUserName(),charMap1)
# concat, hash and encode
DSN = encode(SHA1(MazamaRandomNumber+encodedSystemVolumeSerialNumber+encodedUsername),charMap1)
if verbose >1:
print("DSN: " + DSN)
#
# Compute the device PID
#
table = generatePidEncryptionTable()
devicePID = generateDevicePID(table,DSN,4)
PIDs.append(devicePID)
if verbose > 0:
print("Device PID: " + devicePID)
#
# Open book and parse metadata
#
if len(args) == 1:
bookFile = openBook(args[0])
parseTopazHeader()
parseMetadata()
#
# Compute book PID
#
# Get the account token
if kindleDatabase != None:
kindleAccountToken = getKindleInfoValueForKey("kindle.account.tokens")
if verbose >1:
print("Account Token: " + kindleAccountToken)
keysRecord = bookMetadata["keys"]
keysRecordRecord = bookMetadata[keysRecord]
pidHash = SHA1(DSN+kindleAccountToken+keysRecord+keysRecordRecord)
bookPID = encodePID(pidHash)
PIDs.append(bookPID)
if verbose > 0:
print ("Book PID: " + bookPID )
#
# Decrypt book key
#
dkey = getBookPayloadRecord('dkey', 0)
bookKeys = []
for PID in PIDs :
bookKeys+=decryptDkeyRecords(dkey,PID)
if len(bookKeys) == 0 :
if verbose > 0 :
print ("Book key could not be found. Maybe this book is not registered with this device.")
else :
bookKey = bookKeys[0]
if verbose > 0:
print("Book key: " + bookKey.encode('hex'))
if command == "printRecord" :
extractBookPayloadRecord(recordName,int(recordIndex),outputFile)
if outputFile != "" and verbose>0 :
print("Wrote record to file: "+outputFile)
elif command == "doit" :
if outputFile!="" :
createDecryptedBook(outputFile)
if verbose >0 :
print ("Decrypted book saved. Don't pirate!")
elif verbose > 0:
print("Output file name was not supplied.")
return 0
if __name__ == '__main__':
sys.exit(main())

View File

@ -0,0 +1,59 @@
from PyQt4.Qt import QWidget, QVBoxLayout, QLabel, QLineEdit
from calibre.utils.config import JSONConfig
# This is where all preferences for this plugin will be stored
# You should always prefix your config file name with plugins/,
# so as to ensure you dont accidentally clobber a calibre config file
prefs = JSONConfig('plugins/K4MobiDeDRM')
# Set defaults
prefs.defaults['pids'] = ""
prefs.defaults['serials'] = ""
prefs.defaults['WINEPREFIX'] = None
class ConfigWidget(QWidget):
def __init__(self):
QWidget.__init__(self)
self.l = QVBoxLayout()
self.setLayout(self.l)
self.serialLabel = QLabel('Kindle Serial numbers (separate with commas, no spaces)')
self.l.addWidget(self.serialLabel)
self.serials = QLineEdit(self)
self.serials.setText(prefs['serials'])
self.l.addWidget(self.serials)
self.serialLabel.setBuddy(self.serials)
self.pidLabel = QLabel('Mobipocket PIDs (separate with commas, no spaces)')
self.l.addWidget(self.pidLabel)
self.pids = QLineEdit(self)
self.pids.setText(prefs['pids'])
self.l.addWidget(self.pids)
self.pidLabel.setBuddy(self.serials)
self.wpLabel = QLabel('For Linux only: WINEPREFIX (enter absolute path)')
self.l.addWidget(self.wpLabel)
self.wineprefix = QLineEdit(self)
wineprefix = prefs['WINEPREFIX']
if wineprefix is not None:
self.wineprefix.setText(wineprefix)
else:
self.wineprefix.setText('')
self.l.addWidget(self.wineprefix)
self.wpLabel.setBuddy(self.wineprefix)
def save_settings(self):
prefs['pids'] = str(self.pids.text())
prefs['serials'] = str(self.serials.text())
winepref=str(self.wineprefix.text())
if winepref.strip() != '':
prefs['WINEPREFIX'] = winepref
else:
prefs['WINEPREFIX'] = None

View File

@ -214,6 +214,7 @@ class PageParser(object):
'links.title' : (1, 'text', 0, 0), 'links.title' : (1, 'text', 0, 0),
'links.href' : (1, 'text', 0, 0), 'links.href' : (1, 'text', 0, 0),
'links.type' : (1, 'text', 0, 0), 'links.type' : (1, 'text', 0, 0),
'links.id' : (1, 'number', 0, 0),
'paraCont' : (0, 'number', 1, 1), 'paraCont' : (0, 'number', 1, 1),
'paraCont.rootID' : (1, 'number', 0, 0), 'paraCont.rootID' : (1, 'number', 0, 0),
@ -239,6 +240,7 @@ class PageParser(object):
'group' : (1, 'snippets', 1, 0), 'group' : (1, 'snippets', 1, 0),
'group.type' : (1, 'scalar_text', 0, 0), 'group.type' : (1, 'scalar_text', 0, 0),
'group._tag' : (1, 'scalar_text', 0, 0), 'group._tag' : (1, 'scalar_text', 0, 0),
'group.orientation': (1, 'scalar_text', 0, 0),
'region' : (1, 'snippets', 1, 0), 'region' : (1, 'snippets', 1, 0),
'region.type' : (1, 'scalar_text', 0, 0), 'region.type' : (1, 'scalar_text', 0, 0),
@ -246,7 +248,7 @@ class PageParser(object):
'region.y' : (1, 'scalar_number', 0, 0), 'region.y' : (1, 'scalar_number', 0, 0),
'region.h' : (1, 'scalar_number', 0, 0), 'region.h' : (1, 'scalar_number', 0, 0),
'region.w' : (1, 'scalar_number', 0, 0), 'region.w' : (1, 'scalar_number', 0, 0),
'region.orientation' : (1, 'scalar_number', 0, 0), 'region.orientation' : (1, 'scalar_text', 0, 0),
'empty_text_region' : (1, 'snippets', 1, 0), 'empty_text_region' : (1, 'snippets', 1, 0),

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 B

After

Width:  |  Height:  |  Size: 362 B

View File

@ -0,0 +1,77 @@
#!/usr/bin/python
#
# This is a python script. You need a Python interpreter to run it.
# For example, ActiveState Python, which exists for windows.
#
# Changelog
# 1.00 - Initial version
__version__ = '1.00'
import sys
class Unbuffered:
def __init__(self, stream):
self.stream = stream
def write(self, data):
self.stream.write(data)
self.stream.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
sys.stdout=Unbuffered(sys.stdout)
import os
import struct
import binascii
import kgenpids
import topazextract
import mobidedrm
from alfcrypto import Pukall_Cipher
class DrmException(Exception):
pass
def getK4PCpids(path_to_ebook):
# Return Kindle4PC PIDs. Assumes that the caller checked that we are not on Linux, which will raise an exception
mobi = True
magic3 = file(path_to_ebook,'rb').read(3)
if magic3 == 'TPZ':
mobi = False
if mobi:
mb = mobidedrm.MobiBook(path_to_ebook,False)
else:
mb = topazextract.TopazBook(path_to_ebook)
md1, md2 = mb.getPIDMetaInfo()
return kgenpids.getPidList(md1, md2, True, [], [], [])
def main(argv=sys.argv):
print ('getk4pcpids.py v%(__version__)s. '
'Copyright 2012 Apprentic Alf' % globals())
if len(argv)<2 or len(argv)>3:
print "Gets the possible book-specific PIDs from K4PC for a particular book"
print "Usage:"
print " %s <bookfile> [<outfile>]" % sys.argv[0]
return 1
else:
infile = argv[1]
try:
pidlist = getK4PCpids(infile)
except DrmException, e:
print "Error: %s" % e
return 1
pidstring = ','.join(pidlist)
print "Possible PIDs are: ", pidstring
if len(argv) is 3:
outfile = argv[2]
file(outfile, 'w').write(pidstring)
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@ -1,5 +1,5 @@
#! /usr/bin/env python #! /usr/bin/env python
# ineptpdf.pyw, version 7.9 # ineptpdf.pyw, version 7.11
from __future__ import with_statement from __future__ import with_statement
@ -34,6 +34,8 @@ from __future__ import with_statement
# 7.7 - On Windows try PyCrypto first and OpenSSL next # 7.7 - On Windows try PyCrypto first and OpenSSL next
# 7.8 - Modify interface to allow use of import # 7.8 - Modify interface to allow use of import
# 7.9 - Bug fix for some session key errors when len(bookkey) > length required # 7.9 - Bug fix for some session key errors when len(bookkey) > length required
# 7.10 - Various tweaks to fix minor problems.
# 7.11 - More tweaks to fix minor problems.
""" """
Decrypts Adobe ADEPT-encrypted PDF files. Decrypts Adobe ADEPT-encrypted PDF files.
@ -293,6 +295,7 @@ def _load_crypto_pycrypto():
return self._arc4.decrypt(data) return self._arc4.decrypt(data)
class AES(object): class AES(object):
MODE_CBC = _AES.MODE_CBC
@classmethod @classmethod
def new(cls, userkey, mode, iv): def new(cls, userkey, mode, iv):
self = AES() self = AES()
@ -2199,11 +2202,19 @@ class DecryptionDialog(Tkinter.Frame):
def decryptBook(keypath, inpath, outpath): def decryptBook(keypath, inpath, outpath):
with open(inpath, 'rb') as inf: with open(inpath, 'rb') as inf:
serializer = PDFSerializer(inf, keypath) try:
serializer = PDFSerializer(inf, keypath)
except:
print "Error serializing pdf. Probably wrong key."
return 1
# hope this will fix the 'bad file descriptor' problem # hope this will fix the 'bad file descriptor' problem
with open(outpath, 'wb') as outf: with open(outpath, 'wb') as outf:
# help construct to make sure the method runs to the end # help construct to make sure the method runs to the end
serializer.dump(outf) try:
serializer.dump(outf)
except:
print "error writing pdf."
return 1
return 0 return 0

View File

@ -17,7 +17,7 @@ from __future__ import with_statement
# and many many others # and many many others
__version__ = '4.2' __version__ = '4.4'
class Unbuffered: class Unbuffered:
def __init__(self, stream): def __init__(self, stream):
@ -58,7 +58,7 @@ else:
# borrowed from calibre from calibre/src/calibre/__init__.py # borrowed from calibre from calibre/src/calibre/__init__.py
# added in removal of non-printing chars # added in removal of non-printing chars
# and removal of . at start # and removal of . at start
# convert spaces to underscores # convert underscores to spaces (we're OK with spaces in file names)
def cleanup_name(name): def cleanup_name(name):
_filename_sanitize = re.compile(r'[\xae\0\\|\?\*<":>\+/]') _filename_sanitize = re.compile(r'[\xae\0\\|\?\*<":>\+/]')
substitute='_' substitute='_'
@ -73,7 +73,7 @@ def cleanup_name(name):
# Mac and Unix don't like file names that begin with a full stop # Mac and Unix don't like file names that begin with a full stop
if len(one) > 0 and one[0] == '.': if len(one) > 0 and one[0] == '.':
one = substitute+one[1:] one = substitute+one[1:]
one = one.replace(' ','_') one = one.replace('_',' ')
return one return one
def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids): def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
@ -99,11 +99,20 @@ def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
title = mb.getBookTitle() title = mb.getBookTitle()
print "Processing Book: ", title print "Processing Book: ", title
filenametitle = cleanup_name(title) filenametitle = cleanup_name(title)
outfilename = bookname outfilename = cleanup_name(bookname)
if len(outfilename)<=8 or len(filenametitle)<=8:
outfilename = outfilename + "_" + filenametitle # generate 'sensible' filename, that will sort with the original name,
elif outfilename[:8] != filenametitle[:8]: # but is close to the name from the file.
outfilename = outfilename[:8] + "_" + filenametitle outlength = len(outfilename)
comparelength = min(8,min(outlength,len(filenametitle)))
copylength = min(max(outfilename.find(' '),8),len(outfilename))
if outlength==0:
outfilename = filenametitle
elif comparelength > 0:
if outfilename[:comparelength] == filenametitle[:comparelength]:
outfilename = filenametitle
else:
outfilename = outfilename[:copylength] + " " + filenametitle
# avoid excessively long file names # avoid excessively long file names
if len(outfilename)>150: if len(outfilename)>150:

View File

@ -385,17 +385,22 @@ def GetIDString():
if isNewInstall(): if isNewInstall():
mungedmac = GetMACAddressMunged() mungedmac = GetMACAddressMunged()
if len(mungedmac) > 7: if len(mungedmac) > 7:
print('Using Munged MAC Address for ID: '+mungedmac)
return mungedmac return mungedmac
sernum = GetVolumeSerialNumber() sernum = GetVolumeSerialNumber()
if len(sernum) > 7: if len(sernum) > 7:
print('Using Volume Serial Number for ID: '+sernum)
return sernum return sernum
diskpart = GetUserHomeAppSupKindleDirParitionName() diskpart = GetUserHomeAppSupKindleDirParitionName()
uuidnum = GetDiskPartitionUUID(diskpart) uuidnum = GetDiskPartitionUUID(diskpart)
if len(uuidnum) > 7: if len(uuidnum) > 7:
print('Using Disk Partition UUID for ID: '+uuidnum)
return uuidnum return uuidnum
mungedmac = GetMACAddressMunged() mungedmac = GetMACAddressMunged()
if len(mungedmac) > 7: if len(mungedmac) > 7:
print('Using Munged MAC Address for ID: '+mungedmac)
return mungedmac return mungedmac
print('Using Fixed constant 9999999999 for ID.')
return '9999999999' return '9999999999'
@ -498,6 +503,7 @@ def getKindleInfoFiles(kInfoFiles):
for resline in reslst: for resline in reslst:
if os.path.isfile(resline): if os.path.isfile(resline):
kInfoFiles.append(resline) kInfoFiles.append(resline)
print('Found K4Mac kindle-info file: ' + resline)
found = True found = True
# add any .rainier*-kinf files # add any .rainier*-kinf files
cmdline = 'find "' + home + '/Library/Application Support" -name ".rainier*-kinf"' cmdline = 'find "' + home + '/Library/Application Support" -name ".rainier*-kinf"'
@ -508,6 +514,7 @@ def getKindleInfoFiles(kInfoFiles):
for resline in reslst: for resline in reslst:
if os.path.isfile(resline): if os.path.isfile(resline):
kInfoFiles.append(resline) kInfoFiles.append(resline)
print('Found k4Mac kinf file: ' + resline)
found = True found = True
# add any .kinf2011 files # add any .kinf2011 files
cmdline = 'find "' + home + '/Library/Application Support" -name ".kinf2011"' cmdline = 'find "' + home + '/Library/Application Support" -name ".kinf2011"'
@ -518,9 +525,10 @@ def getKindleInfoFiles(kInfoFiles):
for resline in reslst: for resline in reslst:
if os.path.isfile(resline): if os.path.isfile(resline):
kInfoFiles.append(resline) kInfoFiles.append(resline)
print('Found k4Mac kinf2011 file: ' + resline)
found = True found = True
if not found: if not found:
print('No kindle-info files have been found.') print('No k4Mac kindle-info/kinf/kinf2011 files have been found.')
return kInfoFiles return kInfoFiles
# determine type of kindle info provided and return a # determine type of kindle info provided and return a

View File

@ -151,7 +151,9 @@ def GetVolumeSerialNumber():
GetVolumeSerialNumber = GetVolumeSerialNumber() GetVolumeSerialNumber = GetVolumeSerialNumber()
def GetIDString(): def GetIDString():
return GetVolumeSerialNumber() vsn = GetVolumeSerialNumber()
print('Using Volume Serial Number for ID: '+vsn)
return vsn
def getLastError(): def getLastError():
GetLastError = kernel32.GetLastError GetLastError = kernel32.GetLastError
@ -210,37 +212,40 @@ def getKindleInfoFiles(kInfoFiles):
if 'LOCALAPPDATA' in os.environ.keys(): if 'LOCALAPPDATA' in os.environ.keys():
path = os.environ['LOCALAPPDATA'] path = os.environ['LOCALAPPDATA']
print "searching for kinfoFiles in ", path print('searching for kinfoFiles in ' + path)
found = False
# first look for older kindle-info files # first look for older kindle-info files
kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info' kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info'
if not os.path.isfile(kinfopath): if os.path.isfile(kinfopath):
print('No kindle.info files have not been found.') found = True
else: print('Found K4PC kindle.info file: ' + kinfopath)
kInfoFiles.append(kinfopath) kInfoFiles.append(kinfopath)
# now look for newer (K4PC 1.5.0 and later rainier.2.1.1.kinf file # now look for newer (K4PC 1.5.0 and later rainier.2.1.1.kinf file
kinfopath = path +'\\Amazon\\Kindle For PC\\storage\\rainier.2.1.1.kinf' kinfopath = path +'\\Amazon\\Kindle For PC\\storage\\rainier.2.1.1.kinf'
if not os.path.isfile(kinfopath): if os.path.isfile(kinfopath):
print('No K4PC 1.5.X .kinf files have not been found.') found = True
else: print('Found K4PC 1.5.X kinf file: ' + kinfopath)
kInfoFiles.append(kinfopath) kInfoFiles.append(kinfopath)
# now look for even newer (K4PC 1.6.0 and later) rainier.2.1.1.kinf file # now look for even newer (K4PC 1.6.0 and later) rainier.2.1.1.kinf file
kinfopath = path +'\\Amazon\\Kindle\\storage\\rainier.2.1.1.kinf' kinfopath = path +'\\Amazon\\Kindle\\storage\\rainier.2.1.1.kinf'
if not os.path.isfile(kinfopath): if os.path.isfile(kinfopath):
print('No K4PC 1.6.X .kinf files have not been found.') found = True
else: print('Found K4PC 1.6.X kinf file: ' + kinfopath)
kInfoFiles.append(kinfopath) kInfoFiles.append(kinfopath)
# now look for even newer (K4PC 1.9.0 and later) .kinf2011 file # now look for even newer (K4PC 1.9.0 and later) .kinf2011 file
kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2011' kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2011'
if not os.path.isfile(kinfopath): if os.path.isfile(kinfopath):
print('No K4PC 1.9.X .kinf files have not been found.') found = True
else: print('Found K4PC kinf2011 file: ' + kinfopath)
kInfoFiles.append(kinfopath) kInfoFiles.append(kinfopath)
if not found:
print('No K4PC kindle.info/kinf/kinf2011 files have been found.')
return kInfoFiles return kInfoFiles

View File

@ -262,9 +262,15 @@ def getPidList(md1, md2, k4, pids, serials, kInfoFiles):
if k4: if k4:
kInfoFiles = getKindleInfoFiles(kInfoFiles) kInfoFiles = getKindleInfoFiles(kInfoFiles)
for infoFile in kInfoFiles: for infoFile in kInfoFiles:
pidlst = getK4Pids(pidlst, md1, md2, infoFile) try:
pidlst = getK4Pids(pidlst, md1, md2, infoFile)
except Exception, message:
print("Error getting PIDs from " + infoFile + ": " + message)
for serialnum in serials: for serialnum in serials:
pidlst = getKindlePid(pidlst, md1, md2, serialnum) try:
pidlst = getKindlePid(pidlst, md1, md2, serialnum)
except Exception, message:
print("Error getting PIDs from " + serialnum + ": " + message)
for pid in pids: for pid in pids:
pidlst.append(pid) pidlst.append(pid)
return pidlst return pidlst

View File

@ -0,0 +1,91 @@
#!/usr/bin/python
# Mobipocket PID calculator v0.2 for Amazon Kindle.
# Copyright (c) 2007, 2009 Igor Skochinsky <skochinsky@mail.ru>
# History:
# 0.1 Initial release
# 0.2 Added support for generating PID for iPhone (thanks to mbp)
# 0.3 changed to autoflush stdout, fixed return code usage
class Unbuffered:
def __init__(self, stream):
self.stream = stream
def write(self, data):
self.stream.write(data)
self.stream.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
import sys
sys.stdout=Unbuffered(sys.stdout)
import binascii
if sys.hexversion >= 0x3000000:
print "This script is incompatible with Python 3.x. Please install Python 2.6.x from python.org"
sys.exit(2)
letters = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
def crc32(s):
return (~binascii.crc32(s,-1))&0xFFFFFFFF
def checksumPid(s):
crc = crc32(s)
crc = crc ^ (crc >> 16)
res = s
l = len(letters)
for i in (0,1):
b = crc & 0xff
pos = (b // l) ^ (b % l)
res += letters[pos%l]
crc >>= 8
return res
def pidFromSerial(s, l):
crc = crc32(s)
arr1 = [0]*l
for i in xrange(len(s)):
arr1[i%l] ^= ord(s[i])
crc_bytes = [crc >> 24 & 0xff, crc >> 16 & 0xff, crc >> 8 & 0xff, crc & 0xff]
for i in xrange(l):
arr1[i] ^= crc_bytes[i&3]
pid = ""
for i in xrange(l):
b = arr1[i] & 0xff
pid+=letters[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))]
return pid
def main(argv=sys.argv):
print "Mobipocket PID calculator for Amazon Kindle. Copyright (c) 2007, 2009 Igor Skochinsky"
if len(sys.argv)==2:
serial = sys.argv[1]
else:
print "Usage: kindlepid.py <Kindle Serial Number>/<iPhone/iPod Touch UDID>"
return 1
if len(serial)==16:
if serial.startswith("B"):
print "Kindle serial number detected"
else:
print "Warning: unrecognized serial number. Please recheck input."
return 1
pid = pidFromSerial(serial,7)+"*"
print "Mobipocket PID for Kindle serial# "+serial+" is "+checksumPid(pid)
return 0
elif len(serial)==40:
print "iPhone serial number (UDID) detected"
pid = pidFromSerial(serial,8)
print "Mobipocket PID for iPhone serial# "+serial+" is "+checksumPid(pid)
return 0
else:
print "Warning: unrecognized serial number. Please recheck input."
return 1
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@ -57,8 +57,11 @@
# 0.33 - Performance improvements for large files (concatenation) # 0.33 - Performance improvements for large files (concatenation)
# 0.34 - Performance improvements in decryption (libalfcrypto) # 0.34 - Performance improvements in decryption (libalfcrypto)
# 0.35 - add interface to get mobi_version # 0.35 - add interface to get mobi_version
# 0.36 - fixed problem with TEXtREAd and getBookTitle interface
# 0.37 - Fixed double announcement for stand-alone operation
__version__ = '0.35'
__version__ = '0.37'
import sys import sys
@ -168,9 +171,10 @@ class MobiBook:
off = self.sections[section][0] off = self.sections[section][0]
return self.data_file[off:endoff] return self.data_file[off:endoff]
def __init__(self, infile): def __init__(self, infile, announce = True):
print ('MobiDeDrm v%(__version__)s. ' if announce:
'Copyright 2008-2011 The Dark Reverser et al.' % globals()) print ('MobiDeDrm v%(__version__)s. '
'Copyright 2008-2012 The Dark Reverser et al.' % globals())
# initial sanity check on file # initial sanity check on file
self.data_file = file(infile, 'rb').read() self.data_file = file(infile, 'rb').read()
@ -198,6 +202,7 @@ class MobiBook:
print "Book has format: ", self.magic print "Book has format: ", self.magic
self.extra_data_flags = 0 self.extra_data_flags = 0
self.mobi_length = 0 self.mobi_length = 0
self.mobi_codepage = 1252
self.mobi_version = -1 self.mobi_version = -1
self.meta_array = {} self.meta_array = {}
return return
@ -248,18 +253,19 @@ class MobiBook:
65001 : 'utf-8', 65001 : 'utf-8',
} }
title = '' title = ''
if 503 in self.meta_array: codec = 'windows-1252'
title = self.meta_array[503] if self.magic == 'BOOKMOBI':
else : if 503 in self.meta_array:
toff, tlen = struct.unpack('>II', self.sect[0x54:0x5c]) title = self.meta_array[503]
tend = toff + tlen else:
title = self.sect[toff:tend] toff, tlen = struct.unpack('>II', self.sect[0x54:0x5c])
tend = toff + tlen
title = self.sect[toff:tend]
if self.mobi_codepage in codec_map.keys():
codec = codec_map[self.mobi_codepage]
if title == '': if title == '':
title = self.header[:32] title = self.header[:32]
title = title.split("\0")[0] title = title.split("\0")[0]
codec = 'windows-1252'
if self.mobi_codepage in codec_map.keys():
codec = codec_map[self.mobi_codepage]
return unicode(title, codec).encode('utf-8') return unicode(title, codec).encode('utf-8')
def getPIDMetaInfo(self): def getPIDMetaInfo(self):
@ -375,7 +381,7 @@ class MobiBook:
raise DrmException("Not yet initialised with PID. Must be opened with Mobipocket Reader first.") raise DrmException("Not yet initialised with PID. Must be opened with Mobipocket Reader first.")
found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids) found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids)
if not found_key: if not found_key:
raise DrmException("No key found. Please report this failure for help.") raise DrmException("No key found in " + str(len(goodpids)) + " keys tried. Please report this failure for help.")
# kill the drm keys # kill the drm keys
self.patchSection(0, "\0" * drm_size, drm_ptr) self.patchSection(0, "\0" * drm_size, drm_ptr)
# kill the drm pointers # kill the drm pointers
@ -411,26 +417,26 @@ class MobiBook:
print "done" print "done"
return return
def getUnencryptedBook(infile,pid): def getUnencryptedBook(infile,pid,announce=True):
if not os.path.isfile(infile): if not os.path.isfile(infile):
raise DrmException('Input File Not Found') raise DrmException('Input File Not Found')
book = MobiBook(infile) book = MobiBook(infile,announce)
book.processBook([pid]) book.processBook([pid])
return book.mobi_data return book.mobi_data
def getUnencryptedBookWithList(infile,pidlist): def getUnencryptedBookWithList(infile,pidlist,announce=True):
if not os.path.isfile(infile): if not os.path.isfile(infile):
raise DrmException('Input File Not Found') raise DrmException('Input File Not Found')
book = MobiBook(infile) book = MobiBook(infile, announce)
book.processBook(pidlist) book.processBook(pidlist)
return book.mobi_data return book.mobi_data
def main(argv=sys.argv): def main(argv=sys.argv):
print ('MobiDeDrm v%(__version__)s. ' print ('MobiDeDrm v%(__version__)s. '
'Copyright 2008-2011 The Dark Reverser et al.' % globals()) 'Copyright 2008-2012 The Dark Reverser et al.' % globals())
if len(argv)<3 or len(argv)>4: if len(argv)<3 or len(argv)>4:
print "Removes protection from Kindle/Mobipocket and Kindle/Print Replica ebooks" print "Removes protection from Kindle/Mobipocket, Kindle/KF8 and Kindle/Print Replica ebooks"
print "Usage:" print "Usage:"
print " %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0] print " %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0]
return 1 return 1
@ -442,7 +448,7 @@ def main(argv=sys.argv):
else: else:
pidlist = {} pidlist = {}
try: try:
stripped_file = getUnencryptedBookWithList(infile, pidlist) stripped_file = getUnencryptedBookWithList(infile, pidlist, False)
file(outfile, 'wb').write(stripped_file) file(outfile, 'wb').write(stripped_file)
except DrmException, e: except DrmException, e:
print "Error: %s" % e print "Error: %s" % e

View File

@ -1,444 +0,0 @@
#!/usr/bin/python
#
# This is a python script. You need a Python interpreter to run it.
# For example, ActiveState Python, which exists for windows.
#
# Changelog
# 0.01 - Initial version
# 0.02 - Huffdic compressed books were not properly decrypted
# 0.03 - Wasn't checking MOBI header length
# 0.04 - Wasn't sanity checking size of data record
# 0.05 - It seems that the extra data flags take two bytes not four
# 0.06 - And that low bit does mean something after all :-)
# 0.07 - The extra data flags aren't present in MOBI header < 0xE8 in size
# 0.08 - ...and also not in Mobi header version < 6
# 0.09 - ...but they are there with Mobi header version 6, header size 0xE4!
# 0.10 - Outputs unencrypted files as-is, so that when run as a Calibre
# import filter it works when importing unencrypted files.
# Also now handles encrypted files that don't need a specific PID.
# 0.11 - use autoflushed stdout and proper return values
# 0.12 - Fix for problems with metadata import as Calibre plugin, report errors
# 0.13 - Formatting fixes: retabbed file, removed trailing whitespace
# and extra blank lines, converted CR/LF pairs at ends of each line,
# and other cosmetic fixes.
# 0.14 - Working out when the extra data flags are present has been problematic
# Versions 7 through 9 have tried to tweak the conditions, but have been
# only partially successful. Closer examination of lots of sample
# files reveals that a confusion has arisen because trailing data entries
# are not encrypted, but it turns out that the multibyte entries
# in utf8 file are encrypted. (Although neither kind gets compressed.)
# This knowledge leads to a simplification of the test for the
# trailing data byte flags - version 5 and higher AND header size >= 0xE4.
# 0.15 - Now outputs 'heartbeat', and is also quicker for long files.
# 0.16 - And reverts to 'done' not 'done.' at the end for unswindle compatibility.
# 0.17 - added modifications to support its use as an imported python module
# both inside calibre and also in other places (ie K4DeDRM tools)
# 0.17a- disabled the standalone plugin feature since a plugin can not import
# a plugin
# 0.18 - It seems that multibyte entries aren't encrypted in a v7 file...
# Removed the disabled Calibre plug-in code
# Permit use of 8-digit PIDs
# 0.19 - It seems that multibyte entries aren't encrypted in a v6 file either.
# 0.20 - Correction: It seems that multibyte entries are encrypted in a v6 file.
# 0.21 - Added support for multiple pids
# 0.22 - revised structure to hold MobiBook as a class to allow an extended interface
# 0.23 - fixed problem with older files with no EXTH section
# 0.24 - add support for type 1 encryption and 'TEXtREAd' books as well
# 0.25 - Fixed support for 'BOOKMOBI' type 1 encryption
# 0.26 - Now enables Text-To-Speech flag and sets clipping limit to 100%
# 0.27 - Correct pid metadata token generation to match that used by skindle (Thank You Bart!)
# 0.28 - slight additional changes to metadata token generation (None -> '')
# 0.29 - It seems that the ideas about when multibyte trailing characters were
# included in the encryption were wrong. They are for DOC compressed
# files, but they are not for HUFF/CDIC compress files!
# 0.30 - Modified interface slightly to work better with new calibre plugin style
# 0.31 - The multibyte encrytion info is true for version 7 files too.
# 0.32 - Added support for "Print Replica" Kindle ebooks
__version__ = '0.32'
import sys
class Unbuffered:
def __init__(self, stream):
self.stream = stream
def write(self, data):
self.stream.write(data)
self.stream.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
sys.stdout=Unbuffered(sys.stdout)
import os
import struct
import binascii
class DrmException(Exception):
pass
#
# MobiBook Utility Routines
#
# Implementation of Pukall Cipher 1
def PC1(key, src, decryption=True):
sum1 = 0;
sum2 = 0;
keyXorVal = 0;
if len(key)!=16:
print "Bad key length!"
return None
wkey = []
for i in xrange(8):
wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1]))
dst = ""
for i in xrange(len(src)):
temp1 = 0;
byteXorVal = 0;
for j in xrange(8):
temp1 ^= wkey[j]
sum2 = (sum2+j)*20021 + sum1
sum1 = (temp1*346)&0xFFFF
sum2 = (sum2+sum1)&0xFFFF
temp1 = (temp1*20021+1)&0xFFFF
byteXorVal ^= temp1 ^ sum2
curByte = ord(src[i])
if not decryption:
keyXorVal = curByte * 257;
curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
if decryption:
keyXorVal = curByte * 257;
for j in xrange(8):
wkey[j] ^= keyXorVal;
dst+=chr(curByte)
return dst
def checksumPid(s):
letters = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
crc = (~binascii.crc32(s,-1))&0xFFFFFFFF
crc = crc ^ (crc >> 16)
res = s
l = len(letters)
for i in (0,1):
b = crc & 0xff
pos = (b // l) ^ (b % l)
res += letters[pos%l]
crc >>= 8
return res
def getSizeOfTrailingDataEntries(ptr, size, flags):
def getSizeOfTrailingDataEntry(ptr, size):
bitpos, result = 0, 0
if size <= 0:
return result
while True:
v = ord(ptr[size-1])
result |= (v & 0x7F) << bitpos
bitpos += 7
size -= 1
if (v & 0x80) != 0 or (bitpos >= 28) or (size == 0):
return result
num = 0
testflags = flags >> 1
while testflags:
if testflags & 1:
num += getSizeOfTrailingDataEntry(ptr, size - num)
testflags >>= 1
# Check the low bit to see if there's multibyte data present.
# if multibyte data is included in the encryped data, we'll
# have already cleared this flag.
if flags & 1:
num += (ord(ptr[size - num - 1]) & 0x3) + 1
return num
class MobiBook:
def loadSection(self, section):
if (section + 1 == self.num_sections):
endoff = len(self.data_file)
else:
endoff = self.sections[section + 1][0]
off = self.sections[section][0]
return self.data_file[off:endoff]
def __init__(self, infile):
print ('MobiDeDrm v%(__version__)s. '
'Copyright 2008-2011 The Dark Reverser et al.' % globals())
# initial sanity check on file
self.data_file = file(infile, 'rb').read()
self.mobi_data = ''
self.header = self.data_file[0:78]
if self.header[0x3C:0x3C+8] != 'BOOKMOBI' and self.header[0x3C:0x3C+8] != 'TEXtREAd':
raise DrmException("invalid file format")
self.magic = self.header[0x3C:0x3C+8]
self.crypto_type = -1
# build up section offset and flag info
self.num_sections, = struct.unpack('>H', self.header[76:78])
self.sections = []
for i in xrange(self.num_sections):
offset, a1,a2,a3,a4 = struct.unpack('>LBBBB', self.data_file[78+i*8:78+i*8+8])
flags, val = a1, a2<<16|a3<<8|a4
self.sections.append( (offset, flags, val) )
# parse information from section 0
self.sect = self.loadSection(0)
self.records, = struct.unpack('>H', self.sect[0x8:0x8+2])
self.compression, = struct.unpack('>H', self.sect[0x0:0x0+2])
if self.magic == 'TEXtREAd':
print "Book has format: ", self.magic
self.extra_data_flags = 0
self.mobi_length = 0
self.mobi_version = -1
self.meta_array = {}
return
self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18])
self.mobi_codepage, = struct.unpack('>L',self.sect[0x1c:0x20])
self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C])
print "MOBI header version = %d, length = %d" %(self.mobi_version, self.mobi_length)
self.extra_data_flags = 0
if (self.mobi_length >= 0xE4) and (self.mobi_version >= 5):
self.extra_data_flags, = struct.unpack('>H', self.sect[0xF2:0xF4])
print "Extra Data Flags = %d" % self.extra_data_flags
if (self.compression != 17480):
# multibyte utf8 data is included in the encryption for PalmDoc compression
# so clear that byte so that we leave it to be decrypted.
self.extra_data_flags &= 0xFFFE
# if exth region exists parse it for metadata array
self.meta_array = {}
try:
exth_flag, = struct.unpack('>L', self.sect[0x80:0x84])
exth = 'NONE'
if exth_flag & 0x40:
exth = self.sect[16 + self.mobi_length:]
if (len(exth) >= 4) and (exth[:4] == 'EXTH'):
nitems, = struct.unpack('>I', exth[8:12])
pos = 12
for i in xrange(nitems):
type, size = struct.unpack('>II', exth[pos: pos + 8])
content = exth[pos + 8: pos + size]
self.meta_array[type] = content
# reset the text to speech flag and clipping limit, if present
if type == 401 and size == 9:
# set clipping limit to 100%
self.patchSection(0, "\144", 16 + self.mobi_length + pos + 8)
elif type == 404 and size == 9:
# make sure text to speech is enabled
self.patchSection(0, "\0", 16 + self.mobi_length + pos + 8)
# print type, size, content, content.encode('hex')
pos += size
except:
self.meta_array = {}
pass
self.print_replica = False
def getBookTitle(self):
codec_map = {
1252 : 'windows-1252',
65001 : 'utf-8',
}
title = ''
if 503 in self.meta_array:
title = self.meta_array[503]
else :
toff, tlen = struct.unpack('>II', self.sect[0x54:0x5c])
tend = toff + tlen
title = self.sect[toff:tend]
if title == '':
title = self.header[:32]
title = title.split("\0")[0]
codec = 'windows-1252'
if self.mobi_codepage in codec_map.keys():
codec = codec_map[self.mobi_codepage]
return unicode(title, codec).encode('utf-8')
def getPIDMetaInfo(self):
rec209 = ''
token = ''
if 209 in self.meta_array:
rec209 = self.meta_array[209]
data = rec209
# The 209 data comes in five byte groups. Interpret the last four bytes
# of each group as a big endian unsigned integer to get a key value
# if that key exists in the meta_array, append its contents to the token
for i in xrange(0,len(data),5):
val, = struct.unpack('>I',data[i+1:i+5])
sval = self.meta_array.get(val,'')
token += sval
return rec209, token
def patch(self, off, new):
self.data_file = self.data_file[:off] + new + self.data_file[off+len(new):]
def patchSection(self, section, new, in_off = 0):
if (section + 1 == self.num_sections):
endoff = len(self.data_file)
else:
endoff = self.sections[section + 1][0]
off = self.sections[section][0]
assert off + in_off + len(new) <= endoff
self.patch(off + in_off, new)
def parseDRM(self, data, count, pidlist):
found_key = None
keyvec1 = "\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96"
for pid in pidlist:
bigpid = pid.ljust(16,'\0')
temp_key = PC1(keyvec1, bigpid, False)
temp_key_sum = sum(map(ord,temp_key)) & 0xff
found_key = None
for i in xrange(count):
verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
if cksum == temp_key_sum:
cookie = PC1(temp_key, cookie)
ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie)
if verification == ver and (flags & 0x1F) == 1:
found_key = finalkey
break
if found_key != None:
break
if not found_key:
# Then try the default encoding that doesn't require a PID
pid = "00000000"
temp_key = keyvec1
temp_key_sum = sum(map(ord,temp_key)) & 0xff
for i in xrange(count):
verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
if cksum == temp_key_sum:
cookie = PC1(temp_key, cookie)
ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie)
if verification == ver:
found_key = finalkey
break
return [found_key,pid]
def getMobiFile(self, outpath):
file(outpath,'wb').write(self.mobi_data)
def getPrintReplica(self):
return self.print_replica
def processBook(self, pidlist):
crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2])
print 'Crypto Type is: ', crypto_type
self.crypto_type = crypto_type
if crypto_type == 0:
print "This book is not encrypted."
# we must still check for Print Replica
self.print_replica = (self.loadSection(1)[0:4] == '%MOP')
self.mobi_data = self.data_file
return
if crypto_type != 2 and crypto_type != 1:
raise DrmException("Cannot decode unknown Mobipocket encryption type %d" % crypto_type)
if 406 in self.meta_array:
data406 = self.meta_array[406]
val406, = struct.unpack('>Q',data406)
if val406 != 0:
raise DrmException("Cannot decode library or rented ebooks.")
goodpids = []
for pid in pidlist:
if len(pid)==10:
if checksumPid(pid[0:-2]) != pid:
print "Warning: PID " + pid + " has incorrect checksum, should have been "+checksumPid(pid[0:-2])
goodpids.append(pid[0:-2])
elif len(pid)==8:
goodpids.append(pid)
if self.crypto_type == 1:
t1_keyvec = "QDCVEPMU675RUBSZ"
if self.magic == 'TEXtREAd':
bookkey_data = self.sect[0x0E:0x0E+16]
elif self.mobi_version < 0:
bookkey_data = self.sect[0x90:0x90+16]
else:
bookkey_data = self.sect[self.mobi_length+16:self.mobi_length+32]
pid = "00000000"
found_key = PC1(t1_keyvec, bookkey_data)
else :
# calculate the keys
drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', self.sect[0xA8:0xA8+16])
if drm_count == 0:
raise DrmException("Not yet initialised with PID. Must be opened with Mobipocket Reader first.")
found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids)
if not found_key:
raise DrmException("No key found. Most likely the correct PID has not been given.")
# kill the drm keys
self.patchSection(0, "\0" * drm_size, drm_ptr)
# kill the drm pointers
self.patchSection(0, "\xff" * 4 + "\0" * 12, 0xA8)
if pid=="00000000":
print "File has default encryption, no specific PID."
else:
print "File is encoded with PID "+checksumPid(pid)+"."
# clear the crypto type
self.patchSection(0, "\0" * 2, 0xC)
# decrypt sections
print "Decrypting. Please wait . . .",
self.mobi_data = self.data_file[:self.sections[1][0]]
for i in xrange(1, self.records+1):
data = self.loadSection(i)
extra_size = getSizeOfTrailingDataEntries(data, len(data), self.extra_data_flags)
if i%100 == 0:
print ".",
# print "record %d, extra_size %d" %(i,extra_size)
decoded_data = PC1(found_key, data[0:len(data) - extra_size])
if i==1:
self.print_replica = (decoded_data[0:4] == '%MOP')
self.mobi_data += decoded_data
if extra_size > 0:
self.mobi_data += data[-extra_size:]
if self.num_sections > self.records+1:
self.mobi_data += self.data_file[self.sections[self.records+1][0]:]
print "done"
return
def getUnencryptedBook(infile,pid):
if not os.path.isfile(infile):
raise DrmException('Input File Not Found')
book = MobiBook(infile)
book.processBook([pid])
return book.mobi_data
def getUnencryptedBookWithList(infile,pidlist):
if not os.path.isfile(infile):
raise DrmException('Input File Not Found')
book = MobiBook(infile)
book.processBook(pidlist)
return book.mobi_data
def main(argv=sys.argv):
print ('MobiDeDrm v%(__version__)s. '
'Copyright 2008-2011 The Dark Reverser et al.' % globals())
if len(argv)<3 or len(argv)>4:
print "Removes protection from Kindle/Mobipocket and Kindle/Print Replica ebooks"
print "Usage:"
print " %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0]
return 1
else:
infile = argv[1]
outfile = argv[2]
if len(argv) is 4:
pidlist = argv[3].split(',')
else:
pidlist = {}
try:
stripped_file = getUnencryptedBookWithList(infile, pidlist)
file(outfile, 'wb').write(stripped_file)
except DrmException, e:
print "Error: %s" % e
return 1
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@ -164,6 +164,9 @@ class DocParser(object):
scale = self.pw scale = self.pw
elif attr == 'line-space': elif attr == 'line-space':
scale = self.fontsize * 2.0 scale = self.fontsize * 2.0
if val == "":
val = 0
if not ((attr == 'hang') and (int(val) == 0)) : if not ((attr == 'hang') and (int(val) == 0)) :
pv = float(val)/scale pv = float(val)/scale

View File

@ -31,11 +31,8 @@ class TpzDRMError(Exception):
# local support routines # local support routines
if inCalibre: if inCalibre:
from calibre_plugins.k4mobidedrm import kgenpids from calibre_plugins.k4mobidedrm import kgenpids
from calibre_plugins.k4mobidedrm import genbook
else: else:
import kgenpids import kgenpids
import genbook
# recursive zip creation support routine # recursive zip creation support routine
def zipUpDir(myzip, tdir, localname): def zipUpDir(myzip, tdir, localname):
@ -271,6 +268,11 @@ class TopazBook:
self.createBookDirectory() self.createBookDirectory()
self.extractFiles() self.extractFiles()
print "Successfully Extracted Topaz contents" print "Successfully Extracted Topaz contents"
if inCalibre:
from calibre_plugins.k4mobidedrm import genbook
else:
import genbook
rv = genbook.generateBook(self.outdir, raw, fixedimage) rv = genbook.generateBook(self.outdir, raw, fixedimage)
if rv == 0: if rv == 0:
print "\nBook Successfully generated" print "\nBook Successfully generated"
@ -300,6 +302,11 @@ class TopazBook:
self.createBookDirectory() self.createBookDirectory()
self.extractFiles() self.extractFiles()
print "Successfully Extracted Topaz contents" print "Successfully Extracted Topaz contents"
if inCalibre:
from calibre_plugins.k4mobidedrm import genbook
else:
import genbook
rv = genbook.generateBook(self.outdir, raw, fixedimage) rv = genbook.generateBook(self.outdir, raw, fixedimage)
if rv == 0: if rv == 0:
print "\nBook Successfully generated" print "\nBook Successfully generated"

View File

@ -1,35 +1,29 @@
ReadMe_DeDRM_vX.X_WinApp ReadMe_DeDRM_v5.2_WinApp
----------------------- -----------------------
DeDRM_vX.X_WinApp is a pure python drag and drop application that allows users to drag and drop ebooks or folders of ebooks onto the DeDRM_Drop_Target to have the DRM removed. It repackages the"tools" python software in one easy to use program that remembers preferences and settings. DeDRM_v5.2_WinApp is a pure python drag and drop application that allows users to drag and drop ebooks or folders of ebooks onto the DeDRM_Drop_Target to have the DRM removed. It repackages the"tools" python software in one easy to use program that remembers preferences and settings.
It should work out of the box with Kindle for PC ebooks and Adobe Adept epub and pdf ebooks. It should work out of the box with Kindle for PC ebooks and Adobe Adept epub and pdf ebooks.
To remove the DRM from standalone Kindle ebooks, eReader pdb ebooks, Barnes and Noble epubs, and Mobipocket ebooks requires the user to double-click the DeDRM_Drop_Target and set some additional Preferences including: To remove the DRM from standalone Kindle ebooks, eReader pdb ebooks, Barnes and Noble epubs, and Mobipocket ebooks requires the user to double-click the DeDRM_Drop_Target and set some additional Preferences including:
Kindle 16 digit Serial Number eInk Kindle: 16 digit Serial Number
Barnes & Noble key files (bnepubkey.b64) Barnes & Noble: key file (bnepubkey.b64)
eReader Social DRM: (Name:Last 8 digits of CC number) eReader Social DRM: Name:Last 8 digits of CC number
MobiPocket, Kindle for iPhone/iPad/iPodTouch 10 digit PID MobiPocket: 10 digit PID
Once these preferences have been set, the user can simply drag and drop ebooks onto the DeDRM_Drop_Target to remove the DRM. Once these preferences have been set, the user can simply drag and drop ebooks onto the DeDRM_Drop_Target to remove the DRM.
This program requires that the proper 32 bit version of Python 2.X (tested with Python 2.5 through Python 2.7) and PyCrypto be installed on your computer before it will work. See below for where to get theese programs for Windows. This program requires that the proper 32 bit version of Python 2.X (tested with Python 2.5 through Python 2.7) and PyCrypto be installed on your computer before it will work. See below for where to get theese programs for Windows.
Installation Installation
------------ ------------
1. From tools_vX.X\DeDRM_Applications\, right click on DeDRM_v_X.X_WinApp.zip and fully Extract its contents. 1. In tools_v5.2\DeDRM_Applications\Windows, right click on DeDRM_5.2_Win.zip and fully extract its contents using "Extract All...", saving to your "My Documents" folder.
2. Move the resulting DeDRM_vX.X_WinApp folder to whereever you keep you other programs. 2. Open the DeDRM_5.2_Win folder you've just created, and make a short-cut of the DeDRM_Drop_Target.bat file (right-click/Create Shortcut). Drag the shortcut file onto your Desktop.
(I typically use an "Applications" folder inside of my home directory)
3. Open the folder, and create a short-cut to DeDRM_Drop_Target and move that short-cut to your Desktop.
4. To set the preferences simply double-click on your just created short-cut.
3. To set the preferences simply double-click on your just created short-cut.
If you already have a correct version of Python and PyCrypto installed and in your path, you are ready to go! If you already have a correct version of Python and PyCrypto installed and in your path, you are ready to go!

View File

@ -21,7 +21,7 @@ import re
import simpleprefs import simpleprefs
__version__ = '5.0' __version__ = '5.2'
class DrmException(Exception): class DrmException(Exception):
pass pass
@ -38,7 +38,7 @@ class MainApp(Tk):
['serials', 'seriallist.txt'], ['serials', 'seriallist.txt'],
['sdrms' , 'sdrmlist.txt' ], ['sdrms' , 'sdrmlist.txt' ],
['outdir' , 'outdir.txt' ]] ['outdir' , 'outdir.txt' ]]
self.po = simpleprefs.SimplePrefs('DeDRM',description) self.po = simpleprefs.SimplePrefs("DeDRM",description)
if self.dnd: if self.dnd:
self.cd = ConvDialog(self) self.cd = ConvDialog(self)
prefs = self.getPreferences() prefs = self.getPreferences()
@ -95,7 +95,7 @@ class PrefsDialog(Toplevel):
Toplevel.__init__(self, mainapp) Toplevel.__init__(self, mainapp)
self.withdraw() self.withdraw()
self.protocol("WM_DELETE_WINDOW", self.withdraw) self.protocol("WM_DELETE_WINDOW", self.withdraw)
self.title("DeDRM") self.title("DeDRM " + __version__)
self.prefs_array = prefs_array self.prefs_array = prefs_array
self.status = Tkinter.Label(self, text='Setting Preferences') self.status = Tkinter.Label(self, text='Setting Preferences')
self.status.pack(fill=Tkconstants.X, expand=1) self.status.pack(fill=Tkconstants.X, expand=1)
@ -566,6 +566,7 @@ def main(argv=sys.argv):
infilelst = argv[1:] infilelst = argv[1:]
filenames = [] filenames = []
for infile in infilelst: for infile in infilelst:
infile = infile.decode(sys.getfilesystemencoding())
print infile print infile
infile = infile.replace('"','') infile = infile.replace('"','')
infile = os.path.abspath(infile) infile = os.path.abspath(infile)

View File

@ -0,0 +1,59 @@
from PyQt4.Qt import QWidget, QVBoxLayout, QLabel, QLineEdit
from calibre.utils.config import JSONConfig
# This is where all preferences for this plugin will be stored
# You should always prefix your config file name with plugins/,
# so as to ensure you dont accidentally clobber a calibre config file
prefs = JSONConfig('plugins/K4MobiDeDRM')
# Set defaults
prefs.defaults['pids'] = ""
prefs.defaults['serials'] = ""
prefs.defaults['WINEPREFIX'] = None
class ConfigWidget(QWidget):
def __init__(self):
QWidget.__init__(self)
self.l = QVBoxLayout()
self.setLayout(self.l)
self.serialLabel = QLabel('Kindle Serial numbers (separate with commas, no spaces)')
self.l.addWidget(self.serialLabel)
self.serials = QLineEdit(self)
self.serials.setText(prefs['serials'])
self.l.addWidget(self.serials)
self.serialLabel.setBuddy(self.serials)
self.pidLabel = QLabel('Mobipocket PIDs (separate with commas, no spaces)')
self.l.addWidget(self.pidLabel)
self.pids = QLineEdit(self)
self.pids.setText(prefs['pids'])
self.l.addWidget(self.pids)
self.pidLabel.setBuddy(self.serials)
self.wpLabel = QLabel('For Linux only: WINEPREFIX (enter absolute path)')
self.l.addWidget(self.wpLabel)
self.wineprefix = QLineEdit(self)
wineprefix = prefs['WINEPREFIX']
if wineprefix is not None:
self.wineprefix.setText(wineprefix)
else:
self.wineprefix.setText('')
self.l.addWidget(self.wineprefix)
self.wpLabel.setBuddy(self.wineprefix)
def save_settings(self):
prefs['pids'] = str(self.pids.text())
prefs['serials'] = str(self.serials.text())
winepref=str(self.wineprefix.text())
if winepref.strip() != '':
prefs['WINEPREFIX'] = winepref
else:
prefs['WINEPREFIX'] = None

View File

@ -214,6 +214,7 @@ class PageParser(object):
'links.title' : (1, 'text', 0, 0), 'links.title' : (1, 'text', 0, 0),
'links.href' : (1, 'text', 0, 0), 'links.href' : (1, 'text', 0, 0),
'links.type' : (1, 'text', 0, 0), 'links.type' : (1, 'text', 0, 0),
'links.id' : (1, 'number', 0, 0),
'paraCont' : (0, 'number', 1, 1), 'paraCont' : (0, 'number', 1, 1),
'paraCont.rootID' : (1, 'number', 0, 0), 'paraCont.rootID' : (1, 'number', 0, 0),
@ -239,6 +240,7 @@ class PageParser(object):
'group' : (1, 'snippets', 1, 0), 'group' : (1, 'snippets', 1, 0),
'group.type' : (1, 'scalar_text', 0, 0), 'group.type' : (1, 'scalar_text', 0, 0),
'group._tag' : (1, 'scalar_text', 0, 0), 'group._tag' : (1, 'scalar_text', 0, 0),
'group.orientation': (1, 'scalar_text', 0, 0),
'region' : (1, 'snippets', 1, 0), 'region' : (1, 'snippets', 1, 0),
'region.type' : (1, 'scalar_text', 0, 0), 'region.type' : (1, 'scalar_text', 0, 0),
@ -246,7 +248,7 @@ class PageParser(object):
'region.y' : (1, 'scalar_number', 0, 0), 'region.y' : (1, 'scalar_number', 0, 0),
'region.h' : (1, 'scalar_number', 0, 0), 'region.h' : (1, 'scalar_number', 0, 0),
'region.w' : (1, 'scalar_number', 0, 0), 'region.w' : (1, 'scalar_number', 0, 0),
'region.orientation' : (1, 'scalar_number', 0, 0), 'region.orientation' : (1, 'scalar_text', 0, 0),
'empty_text_region' : (1, 'snippets', 1, 0), 'empty_text_region' : (1, 'snippets', 1, 0),

View File

@ -0,0 +1,77 @@
#!/usr/bin/python
#
# This is a python script. You need a Python interpreter to run it.
# For example, ActiveState Python, which exists for windows.
#
# Changelog
# 1.00 - Initial version
__version__ = '1.00'
import sys
class Unbuffered:
def __init__(self, stream):
self.stream = stream
def write(self, data):
self.stream.write(data)
self.stream.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
sys.stdout=Unbuffered(sys.stdout)
import os
import struct
import binascii
import kgenpids
import topazextract
import mobidedrm
from alfcrypto import Pukall_Cipher
class DrmException(Exception):
pass
def getK4PCpids(path_to_ebook):
# Return Kindle4PC PIDs. Assumes that the caller checked that we are not on Linux, which will raise an exception
mobi = True
magic3 = file(path_to_ebook,'rb').read(3)
if magic3 == 'TPZ':
mobi = False
if mobi:
mb = mobidedrm.MobiBook(path_to_ebook,False)
else:
mb = topazextract.TopazBook(path_to_ebook)
md1, md2 = mb.getPIDMetaInfo()
return kgenpids.getPidList(md1, md2, True, [], [], [])
def main(argv=sys.argv):
print ('getk4pcpids.py v%(__version__)s. '
'Copyright 2012 Apprentic Alf' % globals())
if len(argv)<2 or len(argv)>3:
print "Gets the possible book-specific PIDs from K4PC for a particular book"
print "Usage:"
print " %s <bookfile> [<outfile>]" % sys.argv[0]
return 1
else:
infile = argv[1]
try:
pidlist = getK4PCpids(infile)
except DrmException, e:
print "Error: %s" % e
return 1
pidstring = ','.join(pidlist)
print "Possible PIDs are: ", pidstring
if len(argv) is 3:
outfile = argv[2]
file(outfile, 'w').write(pidstring)
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@ -1,5 +1,5 @@
#! /usr/bin/env python #! /usr/bin/env python
# ineptpdf.pyw, version 7.9 # ineptpdf.pyw, version 7.11
from __future__ import with_statement from __future__ import with_statement
@ -34,6 +34,8 @@ from __future__ import with_statement
# 7.7 - On Windows try PyCrypto first and OpenSSL next # 7.7 - On Windows try PyCrypto first and OpenSSL next
# 7.8 - Modify interface to allow use of import # 7.8 - Modify interface to allow use of import
# 7.9 - Bug fix for some session key errors when len(bookkey) > length required # 7.9 - Bug fix for some session key errors when len(bookkey) > length required
# 7.10 - Various tweaks to fix minor problems.
# 7.11 - More tweaks to fix minor problems.
""" """
Decrypts Adobe ADEPT-encrypted PDF files. Decrypts Adobe ADEPT-encrypted PDF files.
@ -2200,11 +2202,19 @@ class DecryptionDialog(Tkinter.Frame):
def decryptBook(keypath, inpath, outpath): def decryptBook(keypath, inpath, outpath):
with open(inpath, 'rb') as inf: with open(inpath, 'rb') as inf:
serializer = PDFSerializer(inf, keypath) try:
serializer = PDFSerializer(inf, keypath)
except:
print "Error serializing pdf. Probably wrong key."
return 1
# hope this will fix the 'bad file descriptor' problem # hope this will fix the 'bad file descriptor' problem
with open(outpath, 'wb') as outf: with open(outpath, 'wb') as outf:
# help construct to make sure the method runs to the end # help construct to make sure the method runs to the end
serializer.dump(outf) try:
serializer.dump(outf)
except:
print "error writing pdf."
return 1
return 0 return 0

View File

@ -17,7 +17,7 @@ from __future__ import with_statement
# and many many others # and many many others
__version__ = '4.2' __version__ = '4.4'
class Unbuffered: class Unbuffered:
def __init__(self, stream): def __init__(self, stream):
@ -58,7 +58,7 @@ else:
# borrowed from calibre from calibre/src/calibre/__init__.py # borrowed from calibre from calibre/src/calibre/__init__.py
# added in removal of non-printing chars # added in removal of non-printing chars
# and removal of . at start # and removal of . at start
# convert spaces to underscores # convert underscores to spaces (we're OK with spaces in file names)
def cleanup_name(name): def cleanup_name(name):
_filename_sanitize = re.compile(r'[\xae\0\\|\?\*<":>\+/]') _filename_sanitize = re.compile(r'[\xae\0\\|\?\*<":>\+/]')
substitute='_' substitute='_'
@ -73,7 +73,7 @@ def cleanup_name(name):
# Mac and Unix don't like file names that begin with a full stop # Mac and Unix don't like file names that begin with a full stop
if len(one) > 0 and one[0] == '.': if len(one) > 0 and one[0] == '.':
one = substitute+one[1:] one = substitute+one[1:]
one = one.replace(' ','_') one = one.replace('_',' ')
return one return one
def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids): def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
@ -99,11 +99,20 @@ def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
title = mb.getBookTitle() title = mb.getBookTitle()
print "Processing Book: ", title print "Processing Book: ", title
filenametitle = cleanup_name(title) filenametitle = cleanup_name(title)
outfilename = bookname outfilename = cleanup_name(bookname)
if len(outfilename)<=8 or len(filenametitle)<=8:
outfilename = outfilename + "_" + filenametitle # generate 'sensible' filename, that will sort with the original name,
elif outfilename[:8] != filenametitle[:8]: # but is close to the name from the file.
outfilename = outfilename[:8] + "_" + filenametitle outlength = len(outfilename)
comparelength = min(8,min(outlength,len(filenametitle)))
copylength = min(max(outfilename.find(' '),8),len(outfilename))
if outlength==0:
outfilename = filenametitle
elif comparelength > 0:
if outfilename[:comparelength] == filenametitle[:comparelength]:
outfilename = filenametitle
else:
outfilename = outfilename[:copylength] + " " + filenametitle
# avoid excessively long file names # avoid excessively long file names
if len(outfilename)>150: if len(outfilename)>150:

View File

@ -40,7 +40,7 @@ def _load_crypto_libcrypto():
# #
# int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key); # int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);
# #
# note: the ivec string, and output buffer are mutable # note: the ivec string, and output buffer are both mutable
# void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, # void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
# const unsigned long length, const AES_KEY *key, unsigned char *ivec, const int enc); # const unsigned long length, const AES_KEY *key, unsigned char *ivec, const int enc);
@ -385,17 +385,22 @@ def GetIDString():
if isNewInstall(): if isNewInstall():
mungedmac = GetMACAddressMunged() mungedmac = GetMACAddressMunged()
if len(mungedmac) > 7: if len(mungedmac) > 7:
print('Using Munged MAC Address for ID: '+mungedmac)
return mungedmac return mungedmac
sernum = GetVolumeSerialNumber() sernum = GetVolumeSerialNumber()
if len(sernum) > 7: if len(sernum) > 7:
print('Using Volume Serial Number for ID: '+sernum)
return sernum return sernum
diskpart = GetUserHomeAppSupKindleDirParitionName() diskpart = GetUserHomeAppSupKindleDirParitionName()
uuidnum = GetDiskPartitionUUID(diskpart) uuidnum = GetDiskPartitionUUID(diskpart)
if len(uuidnum) > 7: if len(uuidnum) > 7:
print('Using Disk Partition UUID for ID: '+uuidnum)
return uuidnum return uuidnum
mungedmac = GetMACAddressMunged() mungedmac = GetMACAddressMunged()
if len(mungedmac) > 7: if len(mungedmac) > 7:
print('Using Munged MAC Address for ID: '+mungedmac)
return mungedmac return mungedmac
print('Using Fixed constant 9999999999 for ID.')
return '9999999999' return '9999999999'
@ -498,6 +503,7 @@ def getKindleInfoFiles(kInfoFiles):
for resline in reslst: for resline in reslst:
if os.path.isfile(resline): if os.path.isfile(resline):
kInfoFiles.append(resline) kInfoFiles.append(resline)
print('Found K4Mac kindle-info file: ' + resline)
found = True found = True
# add any .rainier*-kinf files # add any .rainier*-kinf files
cmdline = 'find "' + home + '/Library/Application Support" -name ".rainier*-kinf"' cmdline = 'find "' + home + '/Library/Application Support" -name ".rainier*-kinf"'
@ -508,6 +514,7 @@ def getKindleInfoFiles(kInfoFiles):
for resline in reslst: for resline in reslst:
if os.path.isfile(resline): if os.path.isfile(resline):
kInfoFiles.append(resline) kInfoFiles.append(resline)
print('Found k4Mac kinf file: ' + resline)
found = True found = True
# add any .kinf2011 files # add any .kinf2011 files
cmdline = 'find "' + home + '/Library/Application Support" -name ".kinf2011"' cmdline = 'find "' + home + '/Library/Application Support" -name ".kinf2011"'
@ -518,9 +525,10 @@ def getKindleInfoFiles(kInfoFiles):
for resline in reslst: for resline in reslst:
if os.path.isfile(resline): if os.path.isfile(resline):
kInfoFiles.append(resline) kInfoFiles.append(resline)
print('Found k4Mac kinf2011 file: ' + resline)
found = True found = True
if not found: if not found:
print('No kindle-info files have been found.') print('No k4Mac kindle-info/kinf/kinf2011 files have been found.')
return kInfoFiles return kInfoFiles
# determine type of kindle info provided and return a # determine type of kindle info provided and return a

View File

@ -151,7 +151,9 @@ def GetVolumeSerialNumber():
GetVolumeSerialNumber = GetVolumeSerialNumber() GetVolumeSerialNumber = GetVolumeSerialNumber()
def GetIDString(): def GetIDString():
return GetVolumeSerialNumber() vsn = GetVolumeSerialNumber()
print('Using Volume Serial Number for ID: '+vsn)
return vsn
def getLastError(): def getLastError():
GetLastError = kernel32.GetLastError GetLastError = kernel32.GetLastError
@ -210,37 +212,40 @@ def getKindleInfoFiles(kInfoFiles):
if 'LOCALAPPDATA' in os.environ.keys(): if 'LOCALAPPDATA' in os.environ.keys():
path = os.environ['LOCALAPPDATA'] path = os.environ['LOCALAPPDATA']
print "searching for kinfoFiles in ", path print('searching for kinfoFiles in ' + path)
found = False
# first look for older kindle-info files # first look for older kindle-info files
kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info' kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info'
if not os.path.isfile(kinfopath): if os.path.isfile(kinfopath):
print('No kindle.info files have not been found.') found = True
else: print('Found K4PC kindle.info file: ' + kinfopath)
kInfoFiles.append(kinfopath) kInfoFiles.append(kinfopath)
# now look for newer (K4PC 1.5.0 and later rainier.2.1.1.kinf file # now look for newer (K4PC 1.5.0 and later rainier.2.1.1.kinf file
kinfopath = path +'\\Amazon\\Kindle For PC\\storage\\rainier.2.1.1.kinf' kinfopath = path +'\\Amazon\\Kindle For PC\\storage\\rainier.2.1.1.kinf'
if not os.path.isfile(kinfopath): if os.path.isfile(kinfopath):
print('No K4PC 1.5.X .kinf files have not been found.') found = True
else: print('Found K4PC 1.5.X kinf file: ' + kinfopath)
kInfoFiles.append(kinfopath) kInfoFiles.append(kinfopath)
# now look for even newer (K4PC 1.6.0 and later) rainier.2.1.1.kinf file # now look for even newer (K4PC 1.6.0 and later) rainier.2.1.1.kinf file
kinfopath = path +'\\Amazon\\Kindle\\storage\\rainier.2.1.1.kinf' kinfopath = path +'\\Amazon\\Kindle\\storage\\rainier.2.1.1.kinf'
if not os.path.isfile(kinfopath): if os.path.isfile(kinfopath):
print('No K4PC 1.6.X .kinf files have not been found.') found = True
else: print('Found K4PC 1.6.X kinf file: ' + kinfopath)
kInfoFiles.append(kinfopath) kInfoFiles.append(kinfopath)
# now look for even newer (K4PC 1.9.0 and later) .kinf2011 file # now look for even newer (K4PC 1.9.0 and later) .kinf2011 file
kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2011' kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2011'
if not os.path.isfile(kinfopath): if os.path.isfile(kinfopath):
print('No K4PC 1.9.X .kinf files have not been found.') found = True
else: print('Found K4PC kinf2011 file: ' + kinfopath)
kInfoFiles.append(kinfopath) kInfoFiles.append(kinfopath)
if not found:
print('No K4PC kindle.info/kinf/kinf2011 files have been found.')
return kInfoFiles return kInfoFiles

View File

@ -262,9 +262,15 @@ def getPidList(md1, md2, k4, pids, serials, kInfoFiles):
if k4: if k4:
kInfoFiles = getKindleInfoFiles(kInfoFiles) kInfoFiles = getKindleInfoFiles(kInfoFiles)
for infoFile in kInfoFiles: for infoFile in kInfoFiles:
pidlst = getK4Pids(pidlst, md1, md2, infoFile) try:
pidlst = getK4Pids(pidlst, md1, md2, infoFile)
except Exception, message:
print("Error getting PIDs from " + infoFile + ": " + message)
for serialnum in serials: for serialnum in serials:
pidlst = getKindlePid(pidlst, md1, md2, serialnum) try:
pidlst = getKindlePid(pidlst, md1, md2, serialnum)
except Exception, message:
print("Error getting PIDs from " + serialnum + ": " + message)
for pid in pids: for pid in pids:
pidlst.append(pid) pidlst.append(pid)
return pidlst return pidlst

View File

@ -57,8 +57,11 @@
# 0.33 - Performance improvements for large files (concatenation) # 0.33 - Performance improvements for large files (concatenation)
# 0.34 - Performance improvements in decryption (libalfcrypto) # 0.34 - Performance improvements in decryption (libalfcrypto)
# 0.35 - add interface to get mobi_version # 0.35 - add interface to get mobi_version
# 0.36 - fixed problem with TEXtREAd and getBookTitle interface
# 0.37 - Fixed double announcement for stand-alone operation
__version__ = '0.35'
__version__ = '0.37'
import sys import sys
@ -168,9 +171,10 @@ class MobiBook:
off = self.sections[section][0] off = self.sections[section][0]
return self.data_file[off:endoff] return self.data_file[off:endoff]
def __init__(self, infile): def __init__(self, infile, announce = True):
print ('MobiDeDrm v%(__version__)s. ' if announce:
'Copyright 2008-2011 The Dark Reverser et al.' % globals()) print ('MobiDeDrm v%(__version__)s. '
'Copyright 2008-2012 The Dark Reverser et al.' % globals())
# initial sanity check on file # initial sanity check on file
self.data_file = file(infile, 'rb').read() self.data_file = file(infile, 'rb').read()
@ -198,6 +202,7 @@ class MobiBook:
print "Book has format: ", self.magic print "Book has format: ", self.magic
self.extra_data_flags = 0 self.extra_data_flags = 0
self.mobi_length = 0 self.mobi_length = 0
self.mobi_codepage = 1252
self.mobi_version = -1 self.mobi_version = -1
self.meta_array = {} self.meta_array = {}
return return
@ -248,18 +253,19 @@ class MobiBook:
65001 : 'utf-8', 65001 : 'utf-8',
} }
title = '' title = ''
if 503 in self.meta_array: codec = 'windows-1252'
title = self.meta_array[503] if self.magic == 'BOOKMOBI':
else : if 503 in self.meta_array:
toff, tlen = struct.unpack('>II', self.sect[0x54:0x5c]) title = self.meta_array[503]
tend = toff + tlen else:
title = self.sect[toff:tend] toff, tlen = struct.unpack('>II', self.sect[0x54:0x5c])
tend = toff + tlen
title = self.sect[toff:tend]
if self.mobi_codepage in codec_map.keys():
codec = codec_map[self.mobi_codepage]
if title == '': if title == '':
title = self.header[:32] title = self.header[:32]
title = title.split("\0")[0] title = title.split("\0")[0]
codec = 'windows-1252'
if self.mobi_codepage in codec_map.keys():
codec = codec_map[self.mobi_codepage]
return unicode(title, codec).encode('utf-8') return unicode(title, codec).encode('utf-8')
def getPIDMetaInfo(self): def getPIDMetaInfo(self):
@ -375,7 +381,7 @@ class MobiBook:
raise DrmException("Not yet initialised with PID. Must be opened with Mobipocket Reader first.") raise DrmException("Not yet initialised with PID. Must be opened with Mobipocket Reader first.")
found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids) found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids)
if not found_key: if not found_key:
raise DrmException("No key found. Please report this failure for help.") raise DrmException("No key found in " + str(len(goodpids)) + " keys tried. Please report this failure for help.")
# kill the drm keys # kill the drm keys
self.patchSection(0, "\0" * drm_size, drm_ptr) self.patchSection(0, "\0" * drm_size, drm_ptr)
# kill the drm pointers # kill the drm pointers
@ -411,26 +417,26 @@ class MobiBook:
print "done" print "done"
return return
def getUnencryptedBook(infile,pid): def getUnencryptedBook(infile,pid,announce=True):
if not os.path.isfile(infile): if not os.path.isfile(infile):
raise DrmException('Input File Not Found') raise DrmException('Input File Not Found')
book = MobiBook(infile) book = MobiBook(infile,announce)
book.processBook([pid]) book.processBook([pid])
return book.mobi_data return book.mobi_data
def getUnencryptedBookWithList(infile,pidlist): def getUnencryptedBookWithList(infile,pidlist,announce=True):
if not os.path.isfile(infile): if not os.path.isfile(infile):
raise DrmException('Input File Not Found') raise DrmException('Input File Not Found')
book = MobiBook(infile) book = MobiBook(infile, announce)
book.processBook(pidlist) book.processBook(pidlist)
return book.mobi_data return book.mobi_data
def main(argv=sys.argv): def main(argv=sys.argv):
print ('MobiDeDrm v%(__version__)s. ' print ('MobiDeDrm v%(__version__)s. '
'Copyright 2008-2011 The Dark Reverser et al.' % globals()) 'Copyright 2008-2012 The Dark Reverser et al.' % globals())
if len(argv)<3 or len(argv)>4: if len(argv)<3 or len(argv)>4:
print "Removes protection from Kindle/Mobipocket and Kindle/Print Replica ebooks" print "Removes protection from Kindle/Mobipocket, Kindle/KF8 and Kindle/Print Replica ebooks"
print "Usage:" print "Usage:"
print " %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0] print " %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0]
return 1 return 1
@ -442,7 +448,7 @@ def main(argv=sys.argv):
else: else:
pidlist = {} pidlist = {}
try: try:
stripped_file = getUnencryptedBookWithList(infile, pidlist) stripped_file = getUnencryptedBookWithList(infile, pidlist, False)
file(outfile, 'wb').write(stripped_file) file(outfile, 'wb').write(stripped_file)
except DrmException, e: except DrmException, e:
print "Error: %s" % e print "Error: %s" % e

View File

@ -164,6 +164,9 @@ class DocParser(object):
scale = self.pw scale = self.pw
elif attr == 'line-space': elif attr == 'line-space':
scale = self.fontsize * 2.0 scale = self.fontsize * 2.0
if val == "":
val = 0
if not ((attr == 'hang') and (int(val) == 0)) : if not ((attr == 'hang') and (int(val) == 0)) :
pv = float(val)/scale pv = float(val)/scale

View File

@ -31,11 +31,8 @@ class TpzDRMError(Exception):
# local support routines # local support routines
if inCalibre: if inCalibre:
from calibre_plugins.k4mobidedrm import kgenpids from calibre_plugins.k4mobidedrm import kgenpids
from calibre_plugins.k4mobidedrm import genbook
else: else:
import kgenpids import kgenpids
import genbook
# recursive zip creation support routine # recursive zip creation support routine
def zipUpDir(myzip, tdir, localname): def zipUpDir(myzip, tdir, localname):
@ -271,6 +268,11 @@ class TopazBook:
self.createBookDirectory() self.createBookDirectory()
self.extractFiles() self.extractFiles()
print "Successfully Extracted Topaz contents" print "Successfully Extracted Topaz contents"
if inCalibre:
from calibre_plugins.k4mobidedrm import genbook
else:
import genbook
rv = genbook.generateBook(self.outdir, raw, fixedimage) rv = genbook.generateBook(self.outdir, raw, fixedimage)
if rv == 0: if rv == 0:
print "\nBook Successfully generated" print "\nBook Successfully generated"
@ -300,6 +302,11 @@ class TopazBook:
self.createBookDirectory() self.createBookDirectory()
self.extractFiles() self.extractFiles()
print "Successfully Extracted Topaz contents" print "Successfully Extracted Topaz contents"
if inCalibre:
from calibre_plugins.k4mobidedrm import genbook
else:
import genbook
rv = genbook.generateBook(self.outdir, raw, fixedimage) rv = genbook.generateBook(self.outdir, raw, fixedimage)
if rv == 0: if rv == 0:
print "\nBook Successfully generated" print "\nBook Successfully generated"

View File

@ -27,8 +27,8 @@
# files reveals that a confusion has arisen because trailing data entries # files reveals that a confusion has arisen because trailing data entries
# are not encrypted, but it turns out that the multibyte entries # are not encrypted, but it turns out that the multibyte entries
# in utf8 file are encrypted. (Although neither kind gets compressed.) # in utf8 file are encrypted. (Although neither kind gets compressed.)
# This knowledge leads to a simplification of the test for the # This knowledge leads to a simplification of the test for the
# trailing data byte flags - version 5 and higher AND header size >= 0xE4. # trailing data byte flags - version 5 and higher AND header size >= 0xE4.
# 0.15 - Now outputs 'heartbeat', and is also quicker for long files. # 0.15 - Now outputs 'heartbeat', and is also quicker for long files.
# 0.16 - And reverts to 'done' not 'done.' at the end for unswindle compatibility. # 0.16 - And reverts to 'done' not 'done.' at the end for unswindle compatibility.
# 0.17 - added modifications to support its use as an imported python module # 0.17 - added modifications to support its use as an imported python module
@ -42,7 +42,7 @@
# 0.20 - Correction: It seems that multibyte entries are encrypted in a v6 file. # 0.20 - Correction: It seems that multibyte entries are encrypted in a v6 file.
# 0.21 - Added support for multiple pids # 0.21 - Added support for multiple pids
# 0.22 - revised structure to hold MobiBook as a class to allow an extended interface # 0.22 - revised structure to hold MobiBook as a class to allow an extended interface
# 0.23 - fixed problem with older files with no EXTH section # 0.23 - fixed problem with older files with no EXTH section
# 0.24 - add support for type 1 encryption and 'TEXtREAd' books as well # 0.24 - add support for type 1 encryption and 'TEXtREAd' books as well
# 0.25 - Fixed support for 'BOOKMOBI' type 1 encryption # 0.25 - Fixed support for 'BOOKMOBI' type 1 encryption
# 0.26 - Now enables Text-To-Speech flag and sets clipping limit to 100% # 0.26 - Now enables Text-To-Speech flag and sets clipping limit to 100%
@ -55,8 +55,13 @@
# 0.31 - The multibyte encrytion info is true for version 7 files too. # 0.31 - The multibyte encrytion info is true for version 7 files too.
# 0.32 - Added support for "Print Replica" Kindle ebooks # 0.32 - Added support for "Print Replica" Kindle ebooks
# 0.33 - Performance improvements for large files (concatenation) # 0.33 - Performance improvements for large files (concatenation)
# 0.34 - Performance improvements in decryption (libalfcrypto)
# 0.35 - add interface to get mobi_version
# 0.36 - fixed problem with TEXtREAd and getBookTitle interface
# 0.37 - Fixed double announcement for stand-alone operation
__version__ = '0.33'
__version__ = '0.37'
import sys import sys
@ -73,6 +78,7 @@ sys.stdout=Unbuffered(sys.stdout)
import os import os
import struct import struct
import binascii import binascii
from alfcrypto import Pukall_Cipher
class DrmException(Exception): class DrmException(Exception):
pass pass
@ -84,36 +90,37 @@ class DrmException(Exception):
# Implementation of Pukall Cipher 1 # Implementation of Pukall Cipher 1
def PC1(key, src, decryption=True): def PC1(key, src, decryption=True):
sum1 = 0; return Pukall_Cipher().PC1(key,src,decryption)
sum2 = 0; # sum1 = 0;
keyXorVal = 0; # sum2 = 0;
if len(key)!=16: # keyXorVal = 0;
print "Bad key length!" # if len(key)!=16:
return None # print "Bad key length!"
wkey = [] # return None
for i in xrange(8): # wkey = []
wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1])) # for i in xrange(8):
dst = "" # wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1]))
for i in xrange(len(src)): # dst = ""
temp1 = 0; # for i in xrange(len(src)):
byteXorVal = 0; # temp1 = 0;
for j in xrange(8): # byteXorVal = 0;
temp1 ^= wkey[j] # for j in xrange(8):
sum2 = (sum2+j)*20021 + sum1 # temp1 ^= wkey[j]
sum1 = (temp1*346)&0xFFFF # sum2 = (sum2+j)*20021 + sum1
sum2 = (sum2+sum1)&0xFFFF # sum1 = (temp1*346)&0xFFFF
temp1 = (temp1*20021+1)&0xFFFF # sum2 = (sum2+sum1)&0xFFFF
byteXorVal ^= temp1 ^ sum2 # temp1 = (temp1*20021+1)&0xFFFF
curByte = ord(src[i]) # byteXorVal ^= temp1 ^ sum2
if not decryption: # curByte = ord(src[i])
keyXorVal = curByte * 257; # if not decryption:
curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF # keyXorVal = curByte * 257;
if decryption: # curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
keyXorVal = curByte * 257; # if decryption:
for j in xrange(8): # keyXorVal = curByte * 257;
wkey[j] ^= keyXorVal; # for j in xrange(8):
dst+=chr(curByte) # wkey[j] ^= keyXorVal;
return dst # dst+=chr(curByte)
# return dst
def checksumPid(s): def checksumPid(s):
letters = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789" letters = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
@ -164,9 +171,10 @@ class MobiBook:
off = self.sections[section][0] off = self.sections[section][0]
return self.data_file[off:endoff] return self.data_file[off:endoff]
def __init__(self, infile): def __init__(self, infile, announce = True):
print ('MobiDeDrm v%(__version__)s. ' if announce:
'Copyright 2008-2011 The Dark Reverser et al.' % globals()) print ('MobiDeDrm v%(__version__)s. '
'Copyright 2008-2012 The Dark Reverser et al.' % globals())
# initial sanity check on file # initial sanity check on file
self.data_file = file(infile, 'rb').read() self.data_file = file(infile, 'rb').read()
@ -194,6 +202,7 @@ class MobiBook:
print "Book has format: ", self.magic print "Book has format: ", self.magic
self.extra_data_flags = 0 self.extra_data_flags = 0
self.mobi_length = 0 self.mobi_length = 0
self.mobi_codepage = 1252
self.mobi_version = -1 self.mobi_version = -1
self.meta_array = {} self.meta_array = {}
return return
@ -237,25 +246,26 @@ class MobiBook:
self.meta_array = {} self.meta_array = {}
pass pass
self.print_replica = False self.print_replica = False
def getBookTitle(self): def getBookTitle(self):
codec_map = { codec_map = {
1252 : 'windows-1252', 1252 : 'windows-1252',
65001 : 'utf-8', 65001 : 'utf-8',
} }
title = '' title = ''
if 503 in self.meta_array: codec = 'windows-1252'
title = self.meta_array[503] if self.magic == 'BOOKMOBI':
else : if 503 in self.meta_array:
toff, tlen = struct.unpack('>II', self.sect[0x54:0x5c]) title = self.meta_array[503]
tend = toff + tlen else:
title = self.sect[toff:tend] toff, tlen = struct.unpack('>II', self.sect[0x54:0x5c])
tend = toff + tlen
title = self.sect[toff:tend]
if self.mobi_codepage in codec_map.keys():
codec = codec_map[self.mobi_codepage]
if title == '': if title == '':
title = self.header[:32] title = self.header[:32]
title = title.split("\0")[0] title = title.split("\0")[0]
codec = 'windows-1252'
if self.mobi_codepage in codec_map.keys():
codec = codec_map[self.mobi_codepage]
return unicode(title, codec).encode('utf-8') return unicode(title, codec).encode('utf-8')
def getPIDMetaInfo(self): def getPIDMetaInfo(self):
@ -320,6 +330,9 @@ class MobiBook:
def getMobiFile(self, outpath): def getMobiFile(self, outpath):
file(outpath,'wb').write(self.mobi_data) file(outpath,'wb').write(self.mobi_data)
def getMobiVersion(self):
return self.mobi_version
def getPrintReplica(self): def getPrintReplica(self):
return self.print_replica return self.print_replica
@ -356,9 +369,9 @@ class MobiBook:
if self.magic == 'TEXtREAd': if self.magic == 'TEXtREAd':
bookkey_data = self.sect[0x0E:0x0E+16] bookkey_data = self.sect[0x0E:0x0E+16]
elif self.mobi_version < 0: elif self.mobi_version < 0:
bookkey_data = self.sect[0x90:0x90+16] bookkey_data = self.sect[0x90:0x90+16]
else: else:
bookkey_data = self.sect[self.mobi_length+16:self.mobi_length+32] bookkey_data = self.sect[self.mobi_length+16:self.mobi_length+32]
pid = "00000000" pid = "00000000"
found_key = PC1(t1_keyvec, bookkey_data) found_key = PC1(t1_keyvec, bookkey_data)
else : else :
@ -368,12 +381,12 @@ class MobiBook:
raise DrmException("Not yet initialised with PID. Must be opened with Mobipocket Reader first.") raise DrmException("Not yet initialised with PID. Must be opened with Mobipocket Reader first.")
found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids) found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids)
if not found_key: if not found_key:
raise DrmException("No key found. Most likely the correct PID has not been given.") raise DrmException("No key found in " + str(len(goodpids)) + " keys tried. Please report this failure for help.")
# kill the drm keys # kill the drm keys
self.patchSection(0, "\0" * drm_size, drm_ptr) self.patchSection(0, "\0" * drm_size, drm_ptr)
# kill the drm pointers # kill the drm pointers
self.patchSection(0, "\xff" * 4 + "\0" * 12, 0xA8) self.patchSection(0, "\xff" * 4 + "\0" * 12, 0xA8)
if pid=="00000000": if pid=="00000000":
print "File has default encryption, no specific PID." print "File has default encryption, no specific PID."
else: else:
@ -404,26 +417,26 @@ class MobiBook:
print "done" print "done"
return return
def getUnencryptedBook(infile,pid): def getUnencryptedBook(infile,pid,announce=True):
if not os.path.isfile(infile): if not os.path.isfile(infile):
raise DrmException('Input File Not Found') raise DrmException('Input File Not Found')
book = MobiBook(infile) book = MobiBook(infile,announce)
book.processBook([pid]) book.processBook([pid])
return book.mobi_data return book.mobi_data
def getUnencryptedBookWithList(infile,pidlist): def getUnencryptedBookWithList(infile,pidlist,announce=True):
if not os.path.isfile(infile): if not os.path.isfile(infile):
raise DrmException('Input File Not Found') raise DrmException('Input File Not Found')
book = MobiBook(infile) book = MobiBook(infile, announce)
book.processBook(pidlist) book.processBook(pidlist)
return book.mobi_data return book.mobi_data
def main(argv=sys.argv): def main(argv=sys.argv):
print ('MobiDeDrm v%(__version__)s. ' print ('MobiDeDrm v%(__version__)s. '
'Copyright 2008-2011 The Dark Reverser et al.' % globals()) 'Copyright 2008-2012 The Dark Reverser et al.' % globals())
if len(argv)<3 or len(argv)>4: if len(argv)<3 or len(argv)>4:
print "Removes protection from Kindle/Mobipocket and Kindle/Print Replica ebooks" print "Removes protection from Kindle/Mobipocket, Kindle/KF8 and Kindle/Print Replica ebooks"
print "Usage:" print "Usage:"
print " %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0] print " %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0]
return 1 return 1
@ -435,7 +448,7 @@ def main(argv=sys.argv):
else: else:
pidlist = {} pidlist = {}
try: try:
stripped_file = getUnencryptedBookWithList(infile, pidlist) stripped_file = getUnencryptedBookWithList(infile, pidlist, False)
file(outfile, 'wb').write(stripped_file) file(outfile, 'wb').write(stripped_file)
except DrmException, e: except DrmException, e:
print "Error: %s" % e print "Error: %s" % e

View File

@ -1,5 +1,5 @@
#! /usr/bin/env python #! /usr/bin/env python
# ineptpdf.pyw, version 7.9 # ineptpdf.pyw, version 7.11
from __future__ import with_statement from __future__ import with_statement
@ -34,6 +34,8 @@ from __future__ import with_statement
# 7.7 - On Windows try PyCrypto first and OpenSSL next # 7.7 - On Windows try PyCrypto first and OpenSSL next
# 7.8 - Modify interface to allow use of import # 7.8 - Modify interface to allow use of import
# 7.9 - Bug fix for some session key errors when len(bookkey) > length required # 7.9 - Bug fix for some session key errors when len(bookkey) > length required
# 7.10 - Various tweaks to fix minor problems.
# 7.11 - More tweaks to fix minor problems.
""" """
Decrypts Adobe ADEPT-encrypted PDF files. Decrypts Adobe ADEPT-encrypted PDF files.
@ -2200,11 +2202,19 @@ class DecryptionDialog(Tkinter.Frame):
def decryptBook(keypath, inpath, outpath): def decryptBook(keypath, inpath, outpath):
with open(inpath, 'rb') as inf: with open(inpath, 'rb') as inf:
serializer = PDFSerializer(inf, keypath) try:
serializer = PDFSerializer(inf, keypath)
except:
print "Error serializing pdf. Probably wrong key."
return 1
# hope this will fix the 'bad file descriptor' problem # hope this will fix the 'bad file descriptor' problem
with open(outpath, 'wb') as outf: with open(outpath, 'wb') as outf:
# help construct to make sure the method runs to the end # help construct to make sure the method runs to the end
serializer.dump(outf) try:
serializer.dump(outf)
except:
print "error writing pdf."
return 1
return 0 return 0

View File

@ -0,0 +1,59 @@
from PyQt4.Qt import QWidget, QVBoxLayout, QLabel, QLineEdit
from calibre.utils.config import JSONConfig
# This is where all preferences for this plugin will be stored
# You should always prefix your config file name with plugins/,
# so as to ensure you dont accidentally clobber a calibre config file
prefs = JSONConfig('plugins/K4MobiDeDRM')
# Set defaults
prefs.defaults['pids'] = ""
prefs.defaults['serials'] = ""
prefs.defaults['WINEPREFIX'] = None
class ConfigWidget(QWidget):
def __init__(self):
QWidget.__init__(self)
self.l = QVBoxLayout()
self.setLayout(self.l)
self.serialLabel = QLabel('Kindle Serial numbers (separate with commas, no spaces)')
self.l.addWidget(self.serialLabel)
self.serials = QLineEdit(self)
self.serials.setText(prefs['serials'])
self.l.addWidget(self.serials)
self.serialLabel.setBuddy(self.serials)
self.pidLabel = QLabel('Mobipocket PIDs (separate with commas, no spaces)')
self.l.addWidget(self.pidLabel)
self.pids = QLineEdit(self)
self.pids.setText(prefs['pids'])
self.l.addWidget(self.pids)
self.pidLabel.setBuddy(self.serials)
self.wpLabel = QLabel('For Linux only: WINEPREFIX (enter absolute path)')
self.l.addWidget(self.wpLabel)
self.wineprefix = QLineEdit(self)
wineprefix = prefs['WINEPREFIX']
if wineprefix is not None:
self.wineprefix.setText(wineprefix)
else:
self.wineprefix.setText('')
self.l.addWidget(self.wineprefix)
self.wpLabel.setBuddy(self.wineprefix)
def save_settings(self):
prefs['pids'] = str(self.pids.text())
prefs['serials'] = str(self.serials.text())
winepref=str(self.wineprefix.text())
if winepref.strip() != '':
prefs['WINEPREFIX'] = winepref
else:
prefs['WINEPREFIX'] = None

View File

@ -214,6 +214,7 @@ class PageParser(object):
'links.title' : (1, 'text', 0, 0), 'links.title' : (1, 'text', 0, 0),
'links.href' : (1, 'text', 0, 0), 'links.href' : (1, 'text', 0, 0),
'links.type' : (1, 'text', 0, 0), 'links.type' : (1, 'text', 0, 0),
'links.id' : (1, 'number', 0, 0),
'paraCont' : (0, 'number', 1, 1), 'paraCont' : (0, 'number', 1, 1),
'paraCont.rootID' : (1, 'number', 0, 0), 'paraCont.rootID' : (1, 'number', 0, 0),
@ -239,6 +240,7 @@ class PageParser(object):
'group' : (1, 'snippets', 1, 0), 'group' : (1, 'snippets', 1, 0),
'group.type' : (1, 'scalar_text', 0, 0), 'group.type' : (1, 'scalar_text', 0, 0),
'group._tag' : (1, 'scalar_text', 0, 0), 'group._tag' : (1, 'scalar_text', 0, 0),
'group.orientation': (1, 'scalar_text', 0, 0),
'region' : (1, 'snippets', 1, 0), 'region' : (1, 'snippets', 1, 0),
'region.type' : (1, 'scalar_text', 0, 0), 'region.type' : (1, 'scalar_text', 0, 0),
@ -246,7 +248,7 @@ class PageParser(object):
'region.y' : (1, 'scalar_number', 0, 0), 'region.y' : (1, 'scalar_number', 0, 0),
'region.h' : (1, 'scalar_number', 0, 0), 'region.h' : (1, 'scalar_number', 0, 0),
'region.w' : (1, 'scalar_number', 0, 0), 'region.w' : (1, 'scalar_number', 0, 0),
'region.orientation' : (1, 'scalar_number', 0, 0), 'region.orientation' : (1, 'scalar_text', 0, 0),
'empty_text_region' : (1, 'snippets', 1, 0), 'empty_text_region' : (1, 'snippets', 1, 0),

View File

@ -0,0 +1,77 @@
#!/usr/bin/python
#
# This is a python script. You need a Python interpreter to run it.
# For example, ActiveState Python, which exists for windows.
#
# Changelog
# 1.00 - Initial version
__version__ = '1.00'
import sys
class Unbuffered:
def __init__(self, stream):
self.stream = stream
def write(self, data):
self.stream.write(data)
self.stream.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
sys.stdout=Unbuffered(sys.stdout)
import os
import struct
import binascii
import kgenpids
import topazextract
import mobidedrm
from alfcrypto import Pukall_Cipher
class DrmException(Exception):
pass
def getK4PCpids(path_to_ebook):
# Return Kindle4PC PIDs. Assumes that the caller checked that we are not on Linux, which will raise an exception
mobi = True
magic3 = file(path_to_ebook,'rb').read(3)
if magic3 == 'TPZ':
mobi = False
if mobi:
mb = mobidedrm.MobiBook(path_to_ebook,False)
else:
mb = topazextract.TopazBook(path_to_ebook)
md1, md2 = mb.getPIDMetaInfo()
return kgenpids.getPidList(md1, md2, True, [], [], [])
def main(argv=sys.argv):
print ('getk4pcpids.py v%(__version__)s. '
'Copyright 2012 Apprentic Alf' % globals())
if len(argv)<2 or len(argv)>3:
print "Gets the possible book-specific PIDs from K4PC for a particular book"
print "Usage:"
print " %s <bookfile> [<outfile>]" % sys.argv[0]
return 1
else:
infile = argv[1]
try:
pidlist = getK4PCpids(infile)
except DrmException, e:
print "Error: %s" % e
return 1
pidstring = ','.join(pidlist)
print "Possible PIDs are: ", pidstring
if len(argv) is 3:
outfile = argv[2]
file(outfile, 'w').write(pidstring)
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@ -17,7 +17,7 @@ from __future__ import with_statement
# and many many others # and many many others
__version__ = '4.2' __version__ = '4.4'
class Unbuffered: class Unbuffered:
def __init__(self, stream): def __init__(self, stream):
@ -58,7 +58,7 @@ else:
# borrowed from calibre from calibre/src/calibre/__init__.py # borrowed from calibre from calibre/src/calibre/__init__.py
# added in removal of non-printing chars # added in removal of non-printing chars
# and removal of . at start # and removal of . at start
# convert spaces to underscores # convert underscores to spaces (we're OK with spaces in file names)
def cleanup_name(name): def cleanup_name(name):
_filename_sanitize = re.compile(r'[\xae\0\\|\?\*<":>\+/]') _filename_sanitize = re.compile(r'[\xae\0\\|\?\*<":>\+/]')
substitute='_' substitute='_'
@ -73,7 +73,7 @@ def cleanup_name(name):
# Mac and Unix don't like file names that begin with a full stop # Mac and Unix don't like file names that begin with a full stop
if len(one) > 0 and one[0] == '.': if len(one) > 0 and one[0] == '.':
one = substitute+one[1:] one = substitute+one[1:]
one = one.replace(' ','_') one = one.replace('_',' ')
return one return one
def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids): def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
@ -99,11 +99,20 @@ def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
title = mb.getBookTitle() title = mb.getBookTitle()
print "Processing Book: ", title print "Processing Book: ", title
filenametitle = cleanup_name(title) filenametitle = cleanup_name(title)
outfilename = bookname outfilename = cleanup_name(bookname)
if len(outfilename)<=8 or len(filenametitle)<=8:
outfilename = outfilename + "_" + filenametitle # generate 'sensible' filename, that will sort with the original name,
elif outfilename[:8] != filenametitle[:8]: # but is close to the name from the file.
outfilename = outfilename[:8] + "_" + filenametitle outlength = len(outfilename)
comparelength = min(8,min(outlength,len(filenametitle)))
copylength = min(max(outfilename.find(' '),8),len(outfilename))
if outlength==0:
outfilename = filenametitle
elif comparelength > 0:
if outfilename[:comparelength] == filenametitle[:comparelength]:
outfilename = filenametitle
else:
outfilename = outfilename[:copylength] + " " + filenametitle
# avoid excessively long file names # avoid excessively long file names
if len(outfilename)>150: if len(outfilename)>150:

View File

@ -385,17 +385,22 @@ def GetIDString():
if isNewInstall(): if isNewInstall():
mungedmac = GetMACAddressMunged() mungedmac = GetMACAddressMunged()
if len(mungedmac) > 7: if len(mungedmac) > 7:
print('Using Munged MAC Address for ID: '+mungedmac)
return mungedmac return mungedmac
sernum = GetVolumeSerialNumber() sernum = GetVolumeSerialNumber()
if len(sernum) > 7: if len(sernum) > 7:
print('Using Volume Serial Number for ID: '+sernum)
return sernum return sernum
diskpart = GetUserHomeAppSupKindleDirParitionName() diskpart = GetUserHomeAppSupKindleDirParitionName()
uuidnum = GetDiskPartitionUUID(diskpart) uuidnum = GetDiskPartitionUUID(diskpart)
if len(uuidnum) > 7: if len(uuidnum) > 7:
print('Using Disk Partition UUID for ID: '+uuidnum)
return uuidnum return uuidnum
mungedmac = GetMACAddressMunged() mungedmac = GetMACAddressMunged()
if len(mungedmac) > 7: if len(mungedmac) > 7:
print('Using Munged MAC Address for ID: '+mungedmac)
return mungedmac return mungedmac
print('Using Fixed constant 9999999999 for ID.')
return '9999999999' return '9999999999'
@ -498,6 +503,7 @@ def getKindleInfoFiles(kInfoFiles):
for resline in reslst: for resline in reslst:
if os.path.isfile(resline): if os.path.isfile(resline):
kInfoFiles.append(resline) kInfoFiles.append(resline)
print('Found K4Mac kindle-info file: ' + resline)
found = True found = True
# add any .rainier*-kinf files # add any .rainier*-kinf files
cmdline = 'find "' + home + '/Library/Application Support" -name ".rainier*-kinf"' cmdline = 'find "' + home + '/Library/Application Support" -name ".rainier*-kinf"'
@ -508,6 +514,7 @@ def getKindleInfoFiles(kInfoFiles):
for resline in reslst: for resline in reslst:
if os.path.isfile(resline): if os.path.isfile(resline):
kInfoFiles.append(resline) kInfoFiles.append(resline)
print('Found k4Mac kinf file: ' + resline)
found = True found = True
# add any .kinf2011 files # add any .kinf2011 files
cmdline = 'find "' + home + '/Library/Application Support" -name ".kinf2011"' cmdline = 'find "' + home + '/Library/Application Support" -name ".kinf2011"'
@ -518,9 +525,10 @@ def getKindleInfoFiles(kInfoFiles):
for resline in reslst: for resline in reslst:
if os.path.isfile(resline): if os.path.isfile(resline):
kInfoFiles.append(resline) kInfoFiles.append(resline)
print('Found k4Mac kinf2011 file: ' + resline)
found = True found = True
if not found: if not found:
print('No kindle-info files have been found.') print('No k4Mac kindle-info/kinf/kinf2011 files have been found.')
return kInfoFiles return kInfoFiles
# determine type of kindle info provided and return a # determine type of kindle info provided and return a

View File

@ -151,7 +151,9 @@ def GetVolumeSerialNumber():
GetVolumeSerialNumber = GetVolumeSerialNumber() GetVolumeSerialNumber = GetVolumeSerialNumber()
def GetIDString(): def GetIDString():
return GetVolumeSerialNumber() vsn = GetVolumeSerialNumber()
print('Using Volume Serial Number for ID: '+vsn)
return vsn
def getLastError(): def getLastError():
GetLastError = kernel32.GetLastError GetLastError = kernel32.GetLastError
@ -210,37 +212,40 @@ def getKindleInfoFiles(kInfoFiles):
if 'LOCALAPPDATA' in os.environ.keys(): if 'LOCALAPPDATA' in os.environ.keys():
path = os.environ['LOCALAPPDATA'] path = os.environ['LOCALAPPDATA']
print "searching for kinfoFiles in ", path print('searching for kinfoFiles in ' + path)
found = False
# first look for older kindle-info files # first look for older kindle-info files
kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info' kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info'
if not os.path.isfile(kinfopath): if os.path.isfile(kinfopath):
print('No kindle.info files have not been found.') found = True
else: print('Found K4PC kindle.info file: ' + kinfopath)
kInfoFiles.append(kinfopath) kInfoFiles.append(kinfopath)
# now look for newer (K4PC 1.5.0 and later rainier.2.1.1.kinf file # now look for newer (K4PC 1.5.0 and later rainier.2.1.1.kinf file
kinfopath = path +'\\Amazon\\Kindle For PC\\storage\\rainier.2.1.1.kinf' kinfopath = path +'\\Amazon\\Kindle For PC\\storage\\rainier.2.1.1.kinf'
if not os.path.isfile(kinfopath): if os.path.isfile(kinfopath):
print('No K4PC 1.5.X .kinf files have not been found.') found = True
else: print('Found K4PC 1.5.X kinf file: ' + kinfopath)
kInfoFiles.append(kinfopath) kInfoFiles.append(kinfopath)
# now look for even newer (K4PC 1.6.0 and later) rainier.2.1.1.kinf file # now look for even newer (K4PC 1.6.0 and later) rainier.2.1.1.kinf file
kinfopath = path +'\\Amazon\\Kindle\\storage\\rainier.2.1.1.kinf' kinfopath = path +'\\Amazon\\Kindle\\storage\\rainier.2.1.1.kinf'
if not os.path.isfile(kinfopath): if os.path.isfile(kinfopath):
print('No K4PC 1.6.X .kinf files have not been found.') found = True
else: print('Found K4PC 1.6.X kinf file: ' + kinfopath)
kInfoFiles.append(kinfopath) kInfoFiles.append(kinfopath)
# now look for even newer (K4PC 1.9.0 and later) .kinf2011 file # now look for even newer (K4PC 1.9.0 and later) .kinf2011 file
kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2011' kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2011'
if not os.path.isfile(kinfopath): if os.path.isfile(kinfopath):
print('No K4PC 1.9.X .kinf files have not been found.') found = True
else: print('Found K4PC kinf2011 file: ' + kinfopath)
kInfoFiles.append(kinfopath) kInfoFiles.append(kinfopath)
if not found:
print('No K4PC kindle.info/kinf/kinf2011 files have been found.')
return kInfoFiles return kInfoFiles

View File

@ -62,7 +62,7 @@ def encode(data, map):
result += map[Q] result += map[Q]
result += map[R] result += map[R]
return result return result
# Hash the bytes in data and then encode the digest with the characters in map # Hash the bytes in data and then encode the digest with the characters in map
def encodeHash(data,map): def encodeHash(data,map):
return encode(MD5(data),map) return encode(MD5(data),map)
@ -78,11 +78,11 @@ def decode(data,map):
value = (((high * len(map)) ^ 0x80) & 0xFF) + low value = (((high * len(map)) ^ 0x80) & 0xFF) + low
result += pack("B",value) result += pack("B",value)
return result return result
# #
# PID generation routines # PID generation routines
# #
# Returns two bit at offset from a bit field # Returns two bit at offset from a bit field
def getTwoBitsFromBitField(bitField,offset): def getTwoBitsFromBitField(bitField,offset):
byteNumber = offset // 4 byteNumber = offset // 4
@ -91,10 +91,10 @@ def getTwoBitsFromBitField(bitField,offset):
# Returns the six bits at offset from a bit field # Returns the six bits at offset from a bit field
def getSixBitsFromBitField(bitField,offset): def getSixBitsFromBitField(bitField,offset):
offset *= 3 offset *= 3
value = (getTwoBitsFromBitField(bitField,offset) <<4) + (getTwoBitsFromBitField(bitField,offset+1) << 2) +getTwoBitsFromBitField(bitField,offset+2) value = (getTwoBitsFromBitField(bitField,offset) <<4) + (getTwoBitsFromBitField(bitField,offset+1) << 2) +getTwoBitsFromBitField(bitField,offset+2)
return value return value
# 8 bits to six bits encoding from hash to generate PID string # 8 bits to six bits encoding from hash to generate PID string
def encodePID(hash): def encodePID(hash):
global charMap3 global charMap3
@ -121,8 +121,8 @@ def generatePidEncryptionTable() :
def generatePidSeed(table,dsn) : def generatePidSeed(table,dsn) :
value = 0 value = 0
for counter in range (0,4) : for counter in range (0,4) :
index = (ord(dsn[counter]) ^ value) &0xFF index = (ord(dsn[counter]) ^ value) &0xFF
value = (value >> 8) ^ table[index] value = (value >> 8) ^ table[index]
return value return value
# Generate the device PID # Generate the device PID
@ -141,7 +141,7 @@ def generateDevicePID(table,dsn,nbRoll):
return pidAscii return pidAscii
def crc32(s): def crc32(s):
return (~binascii.crc32(s,-1))&0xFFFFFFFF return (~binascii.crc32(s,-1))&0xFFFFFFFF
# convert from 8 digit PID to 10 digit PID with checksum # convert from 8 digit PID to 10 digit PID with checksum
def checksumPid(s): def checksumPid(s):
@ -204,10 +204,10 @@ def getK4Pids(pidlst, rec209, token, kInfoFile):
print(message) print(message)
kindleDatabase = None kindleDatabase = None
pass pass
if kindleDatabase == None : if kindleDatabase == None :
return pidlst return pidlst
try: try:
# Get the Mazama Random number # Get the Mazama Random number
MazamaRandomNumber = kindleDatabase["MazamaRandomNumber"] MazamaRandomNumber = kindleDatabase["MazamaRandomNumber"]
@ -217,7 +217,7 @@ def getK4Pids(pidlst, rec209, token, kInfoFile):
except KeyError: except KeyError:
print "Keys not found in " + kInfoFile print "Keys not found in " + kInfoFile
return pidlst return pidlst
# Get the ID string used # Get the ID string used
encodedIDString = encodeHash(GetIDString(),charMap1) encodedIDString = encodeHash(GetIDString(),charMap1)
@ -226,7 +226,7 @@ def getK4Pids(pidlst, rec209, token, kInfoFile):
# concat, hash and encode to calculate the DSN # concat, hash and encode to calculate the DSN
DSN = encode(SHA1(MazamaRandomNumber+encodedIDString+encodedUsername),charMap1) DSN = encode(SHA1(MazamaRandomNumber+encodedIDString+encodedUsername),charMap1)
# Compute the device PID (for which I can tell, is used for nothing). # Compute the device PID (for which I can tell, is used for nothing).
table = generatePidEncryptionTable() table = generatePidEncryptionTable()
devicePID = generateDevicePID(table,DSN,4) devicePID = generateDevicePID(table,DSN,4)
@ -258,13 +258,19 @@ def getK4Pids(pidlst, rec209, token, kInfoFile):
def getPidList(md1, md2, k4, pids, serials, kInfoFiles): def getPidList(md1, md2, k4, pids, serials, kInfoFiles):
pidlst = [] pidlst = []
if kInfoFiles is None: if kInfoFiles is None:
kInfoFiles = [] kInfoFiles = []
if k4: if k4:
kInfoFiles = getKindleInfoFiles(kInfoFiles) kInfoFiles = getKindleInfoFiles(kInfoFiles)
for infoFile in kInfoFiles: for infoFile in kInfoFiles:
pidlst = getK4Pids(pidlst, md1, md2, infoFile) try:
pidlst = getK4Pids(pidlst, md1, md2, infoFile)
except Exception, message:
print("Error getting PIDs from " + infoFile + ": " + message)
for serialnum in serials: for serialnum in serials:
pidlst = getKindlePid(pidlst, md1, md2, serialnum) try:
pidlst = getKindlePid(pidlst, md1, md2, serialnum)
except Exception, message:
print("Error getting PIDs from " + serialnum + ": " + message)
for pid in pids: for pid in pids:
pidlst.append(pid) pidlst.append(pid)
return pidlst return pidlst

View File

@ -57,8 +57,11 @@
# 0.33 - Performance improvements for large files (concatenation) # 0.33 - Performance improvements for large files (concatenation)
# 0.34 - Performance improvements in decryption (libalfcrypto) # 0.34 - Performance improvements in decryption (libalfcrypto)
# 0.35 - add interface to get mobi_version # 0.35 - add interface to get mobi_version
# 0.36 - fixed problem with TEXtREAd and getBookTitle interface
# 0.37 - Fixed double announcement for stand-alone operation
__version__ = '0.35'
__version__ = '0.37'
import sys import sys
@ -168,9 +171,10 @@ class MobiBook:
off = self.sections[section][0] off = self.sections[section][0]
return self.data_file[off:endoff] return self.data_file[off:endoff]
def __init__(self, infile): def __init__(self, infile, announce = True):
print ('MobiDeDrm v%(__version__)s. ' if announce:
'Copyright 2008-2011 The Dark Reverser et al.' % globals()) print ('MobiDeDrm v%(__version__)s. '
'Copyright 2008-2012 The Dark Reverser et al.' % globals())
# initial sanity check on file # initial sanity check on file
self.data_file = file(infile, 'rb').read() self.data_file = file(infile, 'rb').read()
@ -198,6 +202,7 @@ class MobiBook:
print "Book has format: ", self.magic print "Book has format: ", self.magic
self.extra_data_flags = 0 self.extra_data_flags = 0
self.mobi_length = 0 self.mobi_length = 0
self.mobi_codepage = 1252
self.mobi_version = -1 self.mobi_version = -1
self.meta_array = {} self.meta_array = {}
return return
@ -248,18 +253,19 @@ class MobiBook:
65001 : 'utf-8', 65001 : 'utf-8',
} }
title = '' title = ''
if 503 in self.meta_array: codec = 'windows-1252'
title = self.meta_array[503] if self.magic == 'BOOKMOBI':
else : if 503 in self.meta_array:
toff, tlen = struct.unpack('>II', self.sect[0x54:0x5c]) title = self.meta_array[503]
tend = toff + tlen else:
title = self.sect[toff:tend] toff, tlen = struct.unpack('>II', self.sect[0x54:0x5c])
tend = toff + tlen
title = self.sect[toff:tend]
if self.mobi_codepage in codec_map.keys():
codec = codec_map[self.mobi_codepage]
if title == '': if title == '':
title = self.header[:32] title = self.header[:32]
title = title.split("\0")[0] title = title.split("\0")[0]
codec = 'windows-1252'
if self.mobi_codepage in codec_map.keys():
codec = codec_map[self.mobi_codepage]
return unicode(title, codec).encode('utf-8') return unicode(title, codec).encode('utf-8')
def getPIDMetaInfo(self): def getPIDMetaInfo(self):
@ -375,7 +381,7 @@ class MobiBook:
raise DrmException("Not yet initialised with PID. Must be opened with Mobipocket Reader first.") raise DrmException("Not yet initialised with PID. Must be opened with Mobipocket Reader first.")
found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids) found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids)
if not found_key: if not found_key:
raise DrmException("No key found. Please report this failure for help.") raise DrmException("No key found in " + str(len(goodpids)) + " keys tried. Please report this failure for help.")
# kill the drm keys # kill the drm keys
self.patchSection(0, "\0" * drm_size, drm_ptr) self.patchSection(0, "\0" * drm_size, drm_ptr)
# kill the drm pointers # kill the drm pointers
@ -411,26 +417,26 @@ class MobiBook:
print "done" print "done"
return return
def getUnencryptedBook(infile,pid): def getUnencryptedBook(infile,pid,announce=True):
if not os.path.isfile(infile): if not os.path.isfile(infile):
raise DrmException('Input File Not Found') raise DrmException('Input File Not Found')
book = MobiBook(infile) book = MobiBook(infile,announce)
book.processBook([pid]) book.processBook([pid])
return book.mobi_data return book.mobi_data
def getUnencryptedBookWithList(infile,pidlist): def getUnencryptedBookWithList(infile,pidlist,announce=True):
if not os.path.isfile(infile): if not os.path.isfile(infile):
raise DrmException('Input File Not Found') raise DrmException('Input File Not Found')
book = MobiBook(infile) book = MobiBook(infile, announce)
book.processBook(pidlist) book.processBook(pidlist)
return book.mobi_data return book.mobi_data
def main(argv=sys.argv): def main(argv=sys.argv):
print ('MobiDeDrm v%(__version__)s. ' print ('MobiDeDrm v%(__version__)s. '
'Copyright 2008-2011 The Dark Reverser et al.' % globals()) 'Copyright 2008-2012 The Dark Reverser et al.' % globals())
if len(argv)<3 or len(argv)>4: if len(argv)<3 or len(argv)>4:
print "Removes protection from Kindle/Mobipocket and Kindle/Print Replica ebooks" print "Removes protection from Kindle/Mobipocket, Kindle/KF8 and Kindle/Print Replica ebooks"
print "Usage:" print "Usage:"
print " %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0] print " %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0]
return 1 return 1
@ -442,7 +448,7 @@ def main(argv=sys.argv):
else: else:
pidlist = {} pidlist = {}
try: try:
stripped_file = getUnencryptedBookWithList(infile, pidlist) stripped_file = getUnencryptedBookWithList(infile, pidlist, False)
file(outfile, 'wb').write(stripped_file) file(outfile, 'wb').write(stripped_file)
except DrmException, e: except DrmException, e:
print "Error: %s" % e print "Error: %s" % e

View File

@ -164,6 +164,9 @@ class DocParser(object):
scale = self.pw scale = self.pw
elif attr == 'line-space': elif attr == 'line-space':
scale = self.fontsize * 2.0 scale = self.fontsize * 2.0
if val == "":
val = 0
if not ((attr == 'hang') and (int(val) == 0)) : if not ((attr == 'hang') and (int(val) == 0)) :
pv = float(val)/scale pv = float(val)/scale

View File

@ -31,11 +31,8 @@ class TpzDRMError(Exception):
# local support routines # local support routines
if inCalibre: if inCalibre:
from calibre_plugins.k4mobidedrm import kgenpids from calibre_plugins.k4mobidedrm import kgenpids
from calibre_plugins.k4mobidedrm import genbook
else: else:
import kgenpids import kgenpids
import genbook
# recursive zip creation support routine # recursive zip creation support routine
def zipUpDir(myzip, tdir, localname): def zipUpDir(myzip, tdir, localname):
@ -271,6 +268,11 @@ class TopazBook:
self.createBookDirectory() self.createBookDirectory()
self.extractFiles() self.extractFiles()
print "Successfully Extracted Topaz contents" print "Successfully Extracted Topaz contents"
if inCalibre:
from calibre_plugins.k4mobidedrm import genbook
else:
import genbook
rv = genbook.generateBook(self.outdir, raw, fixedimage) rv = genbook.generateBook(self.outdir, raw, fixedimage)
if rv == 0: if rv == 0:
print "\nBook Successfully generated" print "\nBook Successfully generated"
@ -300,6 +302,11 @@ class TopazBook:
self.createBookDirectory() self.createBookDirectory()
self.extractFiles() self.extractFiles()
print "Successfully Extracted Topaz contents" print "Successfully Extracted Topaz contents"
if inCalibre:
from calibre_plugins.k4mobidedrm import genbook
else:
import genbook
rv = genbook.generateBook(self.outdir, raw, fixedimage) rv = genbook.generateBook(self.outdir, raw, fixedimage)
if rv == 0: if rv == 0:
print "\nBook Successfully generated" print "\nBook Successfully generated"

View File

@ -1,7 +1,7 @@
Kindle for iPhone, iPod Touch, iPad Kindle for iPhone, iPod Touch, iPad
------------------------------------ ------------------------------------
The Kindle application for iOS (iPhone/iPod Touch/iPad) uses a PID derived from the serial number of the iPhone/iPod Touch/iPad. Kindlepid.pyw (see Other_Tools/Additional_Tools) is a python script that turns the serial number into the equivalent PID, which can then be used with the Calibre Plugins and DeDRM Applications. The Kindle application for iOS (iPhone/iPod Touch/iPad) uses a PID derived from the UDID number of the iPhone/iPod Touch/iPad. Kindlepid.pyw (see Other_Tools/Additional_Tools) is a python script that turns the serial number into the equivalent PID, which can then be used with the Calibre Plugins and DeDRM Applications.
So, to remove the DRM from (Mobipocket) Kindle books downloaded to your iPhone/iPodTouch/iPad, youll need the latest tools_vX.X.zip archive and So, to remove the DRM from (Mobipocket) Kindle books downloaded to your iPhone/iPodTouch/iPad, youll need the latest tools_vX.X.zip archive and
some way to extract the book files from the backup of your device on your computer. There are several free tools around to do this. some way to extract the book files from the backup of your device on your computer. There are several free tools around to do this.
@ -19,10 +19,19 @@ You can then add this fixed PID to the DeDRM Applications or Calibre_Plugins or
You next need to find an "iPhone Extractor" or "iPhone Explorer" program for your OS that will allow you to mount your iPhone/iPad/iPod Touch as a usb device on your machine and copy your Kindle ebooks from the device back to your home machine. You next need to find an "iPhone Extractor" or "iPhone Explorer" program for your OS that will allow you to mount your iPhone/iPad/iPod Touch as a usb device on your machine and copy your Kindle ebooks from the device back to your home machine.
IMPORTANT UPDATE
----------------
Apple has recently (2012) restricted access to a device's UDID. This means that recent (2012+) versions of Kindle for iOS no longer use this method of generating an encryption key. If you had already installed and registered Kindle for iOS before this change, the key based on your device's UDID will continue to be used. Otherwise, it is not currently (2012) possible to remove the DRM of Kindle eBooks from a newly installed and registered copy of Kindle for iOS.
--- ---
Kindlefix gives you another way to use the PID generated by kindlepid. Some ebook stores and libraries will accept the PIDs generated by kindlepid (some wont), and you can then download ebooks from the store or library encrypted to work with your Kindle. Except they dont. Theres a flag byte set in encrypted Kindle ebooks, and Kindles and the Kindle app wont read encrypted mobipocket ebooks unless that flag is set. Kindlefix will set that flag for you. If your library have Mobipocket ebooks to lend and will accept your Kindles PID, you can now check out library ebooks, run kindlefix on them, and then read them on your Kindle, and when your loan period ends, theyll automatically become unreadable. Kindlefix gives you another way to use the PID generated by kindlepid. Some ebook stores and libraries will accept the PIDs generated by kindlepid (some wont), and you can then download ebooks from the store or library encrypted to work with your Kindle. Except they dont. Theres a flag byte set in encrypted Kindle ebooks, and Kindles and the Kindle app wont read encrypted mobipocket ebooks unless that flag is set. Kindlefix will set that flag for you. If your library have Mobipocket ebooks to lend and will accept your Kindles PID, you can now check out library ebooks, run kindlefix on them, and then read them on your Kindle, and when your loan period ends, theyll automatically become unreadable.
(2012 note: Few, if any, libraries now use this method of loaning ebooks.)

View File

@ -1,18 +1,21 @@
Welcome to the tools! 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 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 v5.2 archive.
The is archive includes tools to remove DRM from: The is archive includes tools to remove DRM from:
- eReader PDB books
- Kindle ebooks (including Mobi, Topaz, Print Replica and KF8).
- Barnes and Noble ePubs - Barnes and Noble ePubs
- Adobe Digitial Editions ePubs - Adobe Digital Editions ePubs
- Adobe Digitial Editions PDFs - Adobe Digital Editions PDFs
- Kindle/Mobipocket ebooks (including Topaz, Print Replica and KF8). - Mobipocket ebooks
- eReader PDB books
These tools do NOT work with Apple's iBooks FairPlay DRM. These tools do NOT work with Apple's iBooks FairPlay DRM.
The only tool that removes Apple's iBooks Fairplay DRM that is Requiem by Brahms version 3.3 or later. Requiem is NOT included in this tools package. It is under active development because Apple constantly updates its DRM scheme to stop Requiem from working. The only tool that removes Apple's iBooks Fairplay DRM that is Requiem by Brahms version 3.3 or later. Requiem is NOT included in this tools package. It is under active development because Apple constantly updates its DRM scheme to stop Requiem from working.
The latest version as of September 2012 is 3.3.5 and works with iTunes 10.5 and above.
Requiem has a Tor website: http://tag3ulp55xczs3pn.onion. To reach the site using Tor, you will need to install Tor (http://www.torproject.org). If you're willing to sacrifice your anonymity, you can use the regular web with tor2web. Just go to http://tag3ulp55xczs3pn.tor2web.com. Requiem has a Tor website: http://tag3ulp55xczs3pn.onion. To reach the site using Tor, you will need to install Tor (http://www.torproject.org). If you're willing to sacrifice your anonymity, you can use the regular web with tor2web. Just go to http://tag3ulp55xczs3pn.tor2web.com.
@ -20,30 +23,34 @@ Requiem has a Tor website: http://tag3ulp55xczs3pn.onion. To reach the site usin
Calibre Users (Mac OS X, Windows, and Linux) Calibre Users (Mac OS X, Windows, and Linux)
-------------------------------------------- --------------------------------------------
If you are a calibre user, the quickest and easiest way to remove DRM from your ebooks is to open the Calibre_Plugins folder and install each of the plugins following the instructions and configuration directions provided in each plugins' README file. If you are a calibre user, the quickest and easiest way to remove DRM from your ebooks is to install each of the plugins in the Calibre_Plugins folder, following the instructions and configuration directions provided in each plugin's ReadMe file.
Once installed and configured, you can simply add a DRM book to calibre and the DeDRM version will be imported into the calibre database. Note that DRM removal ONLY occurs on import. If you have already imported DRM books you'll need to remove them from calibre and re-import them. Once installed and configured, you can simply add a DRM book to calibre and the DeDRMed version will be imported into the calibre database. Note that DRM removal ONLY occurs on import. If you have already imported DRM books you'll need to remove them from calibre and re-import them.
These plugins work for Windows and Mac OS X. All plugins (including the K4MobiDeDRM plugin when used on books from Kindle for PC under wine) should also work on Linux. Linux users should read the section at the end of this ReadMe. These plugins work for Windows, Mac OS X and Linux. For ebooks from Kindle 4 PC and Adobe Digital Editions, Linux users should read the section at the end of this ReadMe.
DeDRM Application for Mac OS X Users: (Mac OS X 10.5, 10.6, and 10.7) DeDRM application for Mac OS X users: (Mac OS X 10.5 and above)
---------------------------------------------------------------------- ----------------------------------------------------------------------
From the DeDRM_Applications folder, drag the DeDRM_X.X.app.zip droplet to your Desktop. Double-click on it once to unzip it to create the DeDRM X.X.app droplet. Double-click on the droplet once and it will guide you through collecting the data it needs to remove the DRM. Drag the "DeDRM 5.2.app" application from the DeDRM_Applications/Macintosh folder to your Desktop (or your Applications Folder, or anywhere else you find convenient). Double-click on the application to run it and it will guide you through collecting the data it needs to remove the DRM from any of the kinds of DRMed ebook listed in the first section of this ReadMe.
To use it simply drag ebooks or folders containing ebooks onto the DeDRM X.X.app droplet and it will process the ebooks. To use the DeDRM application, simply drag ebooks, or folders containing ebooks, onto the DeDRM application and it will remove the DRM of the kinds listed above.
For more detailed instructions, see the "DeDRM ReadMe.rtf" file in the DeDRM_Applications/Macintosh folder.
DeDRM Application for Windows Users: (Windows XP through Windows 7) DeDRM application for Windows users: (Windows XP through Windows 7)
------------------------------------------------------------------ ------------------------------------------------------------------
***This program requires that Python and PyCrypto be properly installed***. ***This program requires that Python and PyCrypto be properly installed.***
See below for details on which versions are recommended. ***See below for details on recommended versions are where to get them.***
From the DeDRM_Applications folder, fully extract the DeDRM_vX.X_WinApp.zip. Drag the resulting DeDRM_vx.x_WinApp folder to someplace out of the way on your machine. Open the folder and make a short-cut from the DeDRM_Drop_Target onto your Desktop. Double-click on the short-cut and DeDRM will launch and it will guide you through collecting the data it needs to remove the DRM. Unzip the DeDRM_5.2_Win.zip archive that's in the DeDRM_Applications/Windows folder, saving the resulting DeDRM_5.2_Win folder in your "My Documents" folder (or anywhere else you find convenient). Make a short-cut on your Desktop of the DeDRM_Drop_Target.bat file that's in the DeDRM_5.2_Win folder. Double-click on the shortcut and the DeDRM application will run and guide you through collecting the data it needs to remove the DRM from any of the kinds of DRMed ebook listed in the first section of this ReadMe.
To use it simply drag ebooks or folders containing ebooks onto the DeDRM_Drop_Target short-cut, and it will process the ebooks. To use the DeDRM application, simply drag ebooks, or folders containing ebooks, onto the DeDRM_Drop_Target.bat shortcut and it will remove the DRM of the kinds listed above.
For more detailed instructions, see the DeDRM_ReadMe.txt file in the DeDRM_Applications/Windows folder.
@ -77,7 +84,7 @@ Look for a README inside of the relevant folder to get you started.
Additional Tools Additional Tools
---------------- ----------------
Some additional useful tools **unrelated to DRM** are also provided in the "Additional_Tools" folder. There are tools for working with finding Topaz ebooks, unpacking Kindle/Mobipocket ebooks (without DRM) to get to the Mobipocket markup language inside, tools to strip source archive from Kindlegen generated mobis, tools to work with Kindle for iPhone/iPad, etc, and tools to dump the contents of mobi headers to see all EXTH (metadata) and related values. Some additional useful tools **unrelated to DRM** are also provided in the "Additional_Tools" folder inside the "Other_Tools" folder. There are tools for working with finding Topaz ebooks, unpacking Kindle/Mobipocket ebooks (without DRM) to get to the Mobipocket markup language inside, tools to strip source archive from Kindlegen generated mobis, tools to work with Kindle for iPhone/iPad, etc, and tools to dump the contents of mobi headers to see all EXTH (metadata) and related values.
@ -124,6 +131,27 @@ Linux Users Only
Since Kindle for PC and Adobe Digital Editions do not offer native Linux versions, here are instructions for using Windows versions under Wine as well as related instructions for the special way to handle some of these tools: Since Kindle for PC and Adobe Digital Editions do not offer native Linux versions, here are instructions for using Windows versions under Wine as well as related instructions for the special way to handle some of these tools:
Linux and Kindle for PC
-----------------------
It is possible to run the Kindle for PC application under Wine.
1. Install a recent version of Wine (>=1.3.15)
2. Some versions of winecfg have a bug in setting the volume serial number, so create a .windows-serial file at root of drive_c to set a proper windows volume serial number (8 digit hex value for unsigned integer).
cd ~
cd .wine
cd drive_c
echo deadbeef > .windows-serial
Replace "deadbeef" with whatever hex value you want but I would stay away from the default setting of "ffffffff" which does not seem to work. BTW: deadbeef is itself a valid possible hex value if you want to use it
3. Download and install Kindle for PC under Wine.
Linux and Kindle for PC (Other_Tools/KindleBooks/) Linux and Kindle for PC (Other_Tools/KindleBooks/)
-------------------------------------------------- --------------------------------------------------
@ -227,7 +255,7 @@ this will launch a window with 3 lines
2. input file: drmbook.epub 2. input file: drmbook.epub
3. output file: name-ypu-want_for_free_book.epub 3. output file: name-ypu-want_for_free_book.epub
Also… once you successfully generate your adept.der keyfile using WINE, you can use the regular ineptepub plugin with the standard Linux calibre. Just put the *.der file(s) in your calibre configuration directory. Also… once you successfully generate your adept.der keyfile using Wine, you can use the regular ineptepub plugin with the standard Linux calibre. Just put the *.der file(s) in your calibre configuration directory.
so if you want you can use calibre in Linux: so if you want you can use calibre in Linux:
11. install the plugins from the tools as discribed in the readmes for win 11. install the plugins from the tools as discribed in the readmes for win