]> xmof Git - DeDRM.git/commitdiff
tools v5.2
authorApprentice Alf <apprenticealf@gmail.com>
Sun, 9 Sep 2012 00:45:24 +0000 (01:45 +0100)
committerApprentice Alf <apprenticealf@gmail.com>
Fri, 6 Mar 2015 17:43:57 +0000 (17:43 +0000)
75 files changed:
Calibre_Plugins/Ignobleepub ReadMe.txt
Calibre_Plugins/Ineptepub ReadMe.txt
Calibre_Plugins/Ineptpdf ReadMe.txt
Calibre_Plugins/K4MobiDeDRM ReadMe.txt
Calibre_Plugins/K4MobiDeDRM_plugin/__init__.py
Calibre_Plugins/K4MobiDeDRM_plugin/config.py [new file with mode: 0644]
Calibre_Plugins/K4MobiDeDRM_plugin/convert2xml.py
Calibre_Plugins/K4MobiDeDRM_plugin/getk4pcpids.py [new file with mode: 0644]
Calibre_Plugins/K4MobiDeDRM_plugin/k4mobidedrm_orig.py
Calibre_Plugins/K4MobiDeDRM_plugin/kgenpids.py
Calibre_Plugins/K4MobiDeDRM_plugin/stylexml2css.py
Calibre_Plugins/K4MobiDeDRM_plugin/topazextract.py
Calibre_Plugins/eReaderPDB2PML ReadMe.txt
Calibre_Plugins/ineptpdf_plugin.zip
Calibre_Plugins/ineptpdf_plugin/__init__.py
Calibre_Plugins/k4mobidedrm_plugin.zip
Calibre_Plugins/k4mobidedrm_plugin/k4mutils.py
Calibre_Plugins/k4mobidedrm_plugin/k4pcutils.py
Calibre_Plugins/k4mobidedrm_plugin/mobidedrm.py
DeDRM_Macintosh_Application/DeDRM ReadMe.rtf
DeDRM_Macintosh_Application/DeDRM.app.txt
DeDRM_Macintosh_Application/DeDRM.app/Contents/Info.plist
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/DeDRM Progress Source.zip
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/DeDRM Progress.app/Contents/Info.plist
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/DeDRM Progress.app/Contents/MacOS/DeDRM Progress
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/DeDRM Progress.app/Contents/Resources/DeDRM Progress.icns [new file with mode: 0644]
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/DeDRM Progress.app/Contents/Resources/English.lproj/MainMenu.nib [new file with mode: 0644]
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/DeDRM.icns [new file with mode: 0644]
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/Scripts/main.scpt
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/alfcrypto.dll [new file with mode: 0644]
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/alfcrypto64.dll [new file with mode: 0644]
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/cmbtc_v2.2.py [new file with mode: 0644]
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/config.py [new file with mode: 0644]
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/convert2xml.py
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/droplet.rsrc
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/getk4pcpids.py [new file with mode: 0644]
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/ineptpdf.py
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4mobidedrm.py
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4mutils.py
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4pcutils.py
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/kgenpids.py
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/kindlepid.py [new file with mode: 0644]
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/libalfcrypto32.so [new file with mode: 0644]
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/libalfcrypto64.so [new file with mode: 0644]
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/mobidedrm.py
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/mobidedrm_orig.py [deleted file]
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/stylexml2css.py
DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/topazextract.py
DeDRM_Windows_Application/DeDRM_ReadMe.txt
DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/DeDRM_app.pyw
DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/config.py [new file with mode: 0644]
DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/convert2xml.py
DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/getk4pcpids.py [new file with mode: 0644]
DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/ineptpdf.py
DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/k4mobidedrm.py
DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/k4mutils.py
DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/k4pcutils.py
DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/kgenpids.py
DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/mobidedrm.py
DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/stylexml2css.py
DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/topazextract.py
Other_Tools/Additional_Tools/lib/mobidedrm.py
Other_Tools/Adobe_PDF_Tools/ineptpdf.pyw
Other_Tools/KindleBooks/lib/config.py [new file with mode: 0644]
Other_Tools/KindleBooks/lib/convert2xml.py
Other_Tools/KindleBooks/lib/getk4pcpids.py [new file with mode: 0644]
Other_Tools/KindleBooks/lib/k4mobidedrm.py
Other_Tools/KindleBooks/lib/k4mutils.py
Other_Tools/KindleBooks/lib/k4pcutils.py
Other_Tools/KindleBooks/lib/kgenpids.py
Other_Tools/KindleBooks/lib/mobidedrm.py
Other_Tools/KindleBooks/lib/stylexml2css.py
Other_Tools/KindleBooks/lib/topazextract.py
Other_Tools/README_Kindle_for_iPad_iPhone_iPodTouch.txt
ReadMe_First.txt

index 68aa608ce037d164ca774e0b57ed45c8d37b0e54..697cbdf447f96b766f37a14689ab8a22cf8408bf 100644 (file)
@@ -1,40 +1,38 @@
-Ignoble Epub DeDRM - ignobleepub_vXX_plugin.zip\r\r
-Requires Calibre version 0.6.44 or higher.\r\r
-\r\r
-All credit given to I <3 Cabbages for the original standalone scripts.\r\r
-I had the much easier job of converting them to a Calibre plugin.\r\r
-\r\r
-This plugin is meant to decrypt Barnes & Noble Epubs that are protected\r\r
-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.\r\r
-\r\r
-Installation:\r\r
-\r\r
-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\r\r
-click the 'Add' button. you're done.\r\r
-\r
-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.\r\r
-\r\r
-Configuration:\r\r
-\r\r
-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\r\r
-Calibre's Preferences->Plugins page. Enter the name and credit card number separated by a comma: Your Name,1234123412341234\r\r
-\r\r
-If you've purchased books with more than one credit card, separate that other info with a colon: Your Name,1234123412341234:Other Name,2345234523452345\r\r
-\r\r
-** 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.\r\r
-\r\r
-** 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.\r\r
-\r\r
-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\r\r
-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.\r\r
-\r\r
-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.\r\r
-\r\r
-Troubleshooting:\r\r
-\r\r
-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\r\r
-as well get used to it. ;)\r\r
-\r\r
-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.\r\r
-\r\r
-** 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.\r\r
+Ignoble Epub DeDRM - ignobleepub_v01.6_plugin.zip
+
+All credit given to I♥Cabbages for the original standalone scripts.
+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.
+
+
+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.
+
+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:
+
+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
+
+** 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.
+
+
+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.
+
+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.
+
+
+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 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.
+
+** 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.
+
+
index 9c58cf06c5f2ec486532f71fc77b03eff96662f8..52bece487709a6160a4564cc066eebda5dfbc7ef 100644 (file)
@@ -1,39 +1,39 @@
-Inept Epub DeDRM - ineptepub_vXX_plugin.zip\r\r
-Requires Calibre version 0.6.44 or higher.\r\r
-\r\r
-All credit given to I <3 Cabbages for the original standalone scripts.\r\r
-I had the much easier job of converting them to a Calibre plugin.\r\r
-\r\r
-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.\r\r
-\r\r
-Installation:\r\r
-\r\r
-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.\r
-\r
-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.\r\r
-\r\r
-\r\r
-Configuration:\r\r
-\r\r
-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\r\r
-find the Adobe Digital Editions installation installation.\r\r
-\r\r
-So if you have Adobe Digital Editions installation installed on the same machine as Calibre... you are ready to go. If not... keep reading.\r\r
-\r\r
-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\r\r
-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.\r\r
-\r\r
-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.\r\r
-\r\r
-All keyfiles with a '.der' extension found in Calibre's configuration directory will be used to attempt to decrypt a book.\r\r
-\r\r
-** NOTE ** There is no plugin customization data for the Inept Epub DeDRM plugin.\r\r
-\r\r
-Troubleshooting:\r\r
-\r\r
-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\r\r
-as well get used to it. ;)\r\r
-\r\r
-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.\r\r
-\r\r
-** 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.\r
+Inept Epub DeDRM - ineptepub_v01.7_plugin.zip
+
+All credit given to I♥Cabbages for the original standalone scripts.
+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.
+
+
+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.
+
+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:
+
+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.
+
+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.
+
+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.
+
+
+** NOTE ** There is no plugin customization data for the Inept Epub DeDRM plugin.
+
+
+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 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.
+
+** 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.
+
index 5c4a0266fbf72336d3f13a1d40831505b8ab329b..f3b15a06419b1f58c76385a0e12f1bf962ba8f47 100644 (file)
@@ -1,39 +1,39 @@
-Inept PDF Plugin - ineptpdf_vXX_plugin.zip\r\r
-Requires Calibre version 0.6.44 or higher.\r\r
-\r\r
-All credit given to IHeartCabbages for the original standalone scripts.\r\r
-I had the much easier job of converting them to a Calibre plugin.\r\r
-\r\r
-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.\r\r
-\r\r
-Installation:\r\r
-\r\r
-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.\r
-\r
-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.\r\r
-\r\r
-\r\r
-Configuration:\r\r
-\r\r
-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\r\r
-find the Adobe Digital Editions installation installation.\r\r
-\r\r
-So if you have Adobe Digital Editions installation installed on the same machine as Calibre... you are ready to go. If not... keep reading.\r\r
-\r\r
-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\r\r
-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.\r\r
-\r\r
-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.\r\r
-\r\r
-All keyfiles with a '.der' extension found in Calibre's configuration directory will be used to attempt to decrypt a book.\r\r
-\r\r
-** NOTE ** There is no plugin customization data for the Inept PDF plugin.\r\r
-\r\r
-Troubleshooting:\r\r
-\r\r
-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\r\r
-as well get used to it. ;)\r\r
-\r\r
-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.\r\r
-\r\r
-** 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.\r
+Inept PDF Plugin - ineptpdf_v01.5_plugin.zip
+
+All credit given to I♥Cabbages for the original standalone scripts.
+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.
+
+
+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.
+
+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:
+
+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.
+
+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
+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.
+
+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.
+
+
+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 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.
+
+** 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.
+
index 8725cf44e3785453d4c33e7049c8cf23efc2dc2d..07abb628220fe8273411d21260c400868b4d44f9 100644 (file)
@@ -1,22 +1,37 @@
-Plugin for K4PC, K4Mac, standalone Kindles, Mobi Books, and for Devices with Fixed PIDs.\r
-\r
-This plugin supersedes MobiDeDRM, K4DeDRM, and K4PCDeDRM and K4X plugins.  If you install this plugin, those plugins can be safely removed.\r\r
-\r\r
-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.\r\r
-\r\r
-Installation:\r\r
-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.\r
-\r
-\r
-Configuration:\r\r
-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. \r
-\r
-\r\r
-Troubleshooting:\r\r
-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\r\r
-as well get used to it. ;)\r\r
-\r\r
-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.\r\r
-\r\r
-** 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.\r\r
-\r
+K4MobiDeDRM_v04.4_plugin.zip
+
+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.
+
+Plugin for K4PC, K4Mac, eInk Kindles and Mobipocket.
+
+This plugin supersedes MobiDeDRM, K4DeDRM, and K4PCDeDRM and K4X plugins.  If you install this plugin, those plugins can be safely removed.
+
+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.
+
+
+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.
+
+
+Configuration:
+
+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).
+
+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.
+
+
index a2afd6627fcd2576f062e01eafebefe514d3bde3..7326e0f0088fa8a25128c60ac9b2ca035c33b3e2 100644 (file)
@@ -15,16 +15,16 @@ import re
 from zipfile import ZipFile
 
 class K4DeDRM(FileTypePlugin):
-    name                = 'K4PC, K4Mac, Kindle Mobi and Topaz 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.'
+    name                = 'Kindle and Mobipocket DeDRM' # Name of the plugin
+    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
-    author              = 'DiapDealer, SomeUpdates' # The author of this plugin
-    version             = (0, 4, 2)   # The version number of this plugin
+    author              = 'DiapDealer, SomeUpdates, mdlnx, Apprentice Alf' # The author 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
     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)
-    
+
     def initialize(self):
         """
         Dynamic modules can't be imported/loaded from a zipfile... so this routine
@@ -39,7 +39,7 @@ class K4DeDRM(FileTypePlugin):
         elif isosx:
             names = ['libalfcrypto.dylib']
         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)
         self.alfdir = os.path.join(config_dir, 'alfcrypto')
         if not os.path.exists(self.alfdir):
@@ -62,35 +62,45 @@ class K4DeDRM(FileTypePlugin):
 
         plug_ver = '.'.join(str(self.version).strip('()').replace(' ', '').split(','))
         k4 = True
-        if sys.platform.startswith('linux'):
-            k4 = False
         pids = []
         serials = []
         kInfoFiles = []
+        self.config()
+        
         # Get supplied list of PIDs to try from plugin customization.
-        customvalues = self.site_customization.split(',')
-        for customvalue in customvalues:
-            customvalue = str(customvalue)
-            customvalue = customvalue.strip()
-            if len(customvalue) == 10 or len(customvalue) == 8:
-                pids.append(customvalue)
-            else :
-                if len(customvalue) == 16 and customvalue[0] == 'B':
-                    serials.append(customvalue)
-                else:
-                    print "%s is not a valid Kindle serial number or PID." % str(customvalue)
-
+        pidstringlistt = self.pids_string.split(',')
+        for pid in pidstringlistt:
+            pid = str(pid).strip()
+            if len(pid) == 10 or len(pid) == 8:
+                pids.append(pid)
+            else:
+                if len(pid) > 0:
+                    print "'%s' is not a valid Mobipocket PID." % pid
+                        
+        # For linux, get PIDs by calling the right routines under WINE
+        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.
         try:
-            # Find Calibre's configuration directory.
-            confpath = os.path.split(os.path.split(self.plugin_path)[0])[0]
-            print 'K4MobiDeDRM v%s: Calibre configuration directory = %s' % (plug_ver, confpath)
-            files = os.listdir(confpath)
+            print 'K4MobiDeDRM v%s: Calibre configuration directory = %s' % (plug_ver, config_dir)
+            files = os.listdir(config_dir)
             filefilter = re.compile("\.info$|\.kinf$", re.IGNORECASE)
             files = filter(filefilter.search, files)
             if files:
                 for filename in files:
-                    fpath = os.path.join(confpath, filename)
+                    fpath = os.path.join(config_dir, filename)
                     kInfoFiles.append(fpath)
                 print 'K4MobiDeDRM v%s: Kindle info/kinf file %s found in config folder.' % (plug_ver, filename)
         except IOError:
@@ -152,8 +162,77 @@ class K4DeDRM(FileTypePlugin):
             mb.cleanup()
         return of.name
 
-    def customization_help(self, gui=False):
-        return 'Enter 10 character PIDs and/or Kindle serial numbers, use a comma (no spaces) to separate each PID or SerialNumber from the next.'
+    def WINEgetPIDs(self, infile):
+
+        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):
         ans = {}
@@ -161,4 +240,4 @@ class K4DeDRM(FileTypePlugin):
             for candidate in zf.namelist():
                 if candidate in names:
                     ans[candidate] = zf.read(candidate)
-        return ans
\ No newline at end of file
+        return ans
diff --git a/Calibre_Plugins/K4MobiDeDRM_plugin/config.py b/Calibre_Plugins/K4MobiDeDRM_plugin/config.py
new file mode 100644 (file)
index 0000000..c029760
--- /dev/null
@@ -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
index 5312a38b2d2be6f4fdff8ac2cfe68b16c3960520..c412d7b1ba2e38eb8c61ab119fd2b2877cccde2a 100644 (file)
@@ -214,6 +214,7 @@ class PageParser(object):
         'links.title'  : (1, 'text', 0, 0),
         'links.href'   : (1, 'text', 0, 0),
         'links.type'   : (1, 'text', 0, 0),
+        'links.id'     : (1, 'number', 0, 0),
 
         'paraCont'          : (0, 'number', 1, 1),
         'paraCont.rootID'   : (1, 'number', 0, 0),
@@ -239,6 +240,7 @@ class PageParser(object):
         'group'           : (1, 'snippets', 1, 0),
         'group.type'      : (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.type'      : (1, 'scalar_text', 0, 0),
@@ -246,7 +248,7 @@ class PageParser(object):
         'region.y'         : (1, 'scalar_number', 0, 0),
         'region.h'         : (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),
 
diff --git a/Calibre_Plugins/K4MobiDeDRM_plugin/getk4pcpids.py b/Calibre_Plugins/K4MobiDeDRM_plugin/getk4pcpids.py
new file mode 100644 (file)
index 0000000..c4716bd
--- /dev/null
@@ -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())
index a889564b8a4b2c8658243bf5eb5e03d2610ea0d2..b5c2804286b3d8102729ac9e6cf8ea110345fdde 100644 (file)
@@ -17,7 +17,7 @@ from __future__ import with_statement
 #    and many many others
 
 
-__version__ = '4.2'
+__version__ = '4.3'
 
 class Unbuffered:
     def __init__(self, stream):
@@ -58,7 +58,7 @@ else:
 # borrowed from calibre from calibre/src/calibre/__init__.py
 # added in removal of non-printing chars
 # 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):
     _filename_sanitize = re.compile(r'[\xae\0\\|\?\*<":>\+/]')
     substitute='_'
@@ -73,7 +73,7 @@ def cleanup_name(name):
     # Mac and Unix don't like file names that begin with a full stop
     if len(one) > 0 and one[0] == '.':
         one = substitute+one[1:]
-    one = one.replace(' ','_')
+    one = one.replace('_',' ')
     return one
 
 def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
@@ -99,11 +99,20 @@ def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
     title = mb.getBookTitle()
     print "Processing Book: ", title
     filenametitle = cleanup_name(title)
-    outfilename = bookname
-    if len(outfilename)<=8 or len(filenametitle)<=8:
-        outfilename = outfilename + "_" + filenametitle
-    elif outfilename[:8] != filenametitle[:8]:
-        outfilename = outfilename[:8] + "_" + filenametitle
+    outfilename = cleanup_name(bookname)
+    
+    # generate 'sensible' filename, that will sort with the original name,
+    # but is close to the name from the file.
+    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
     if len(outfilename)>150:
index c4e45be1daedaaa1a7ca1a0af4da6c2dc1745202..40d84ad713c427faa5b25b18a9a4a2cdcf100a6e 100644 (file)
@@ -262,9 +262,15 @@ def getPidList(md1, md2, k4, pids, serials, kInfoFiles):
     if k4:
         kInfoFiles = getKindleInfoFiles(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:
-        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:
         pidlst.append(pid)
     return pidlst
index adbac4988187e1dc4eb4ce403eb931039069ff6b..2347f6aeb128bd5cdfc22f8d52dc5d8c05ba63b2 100644 (file)
@@ -164,6 +164,9 @@ class DocParser(object):
                                 scale = self.pw
                             elif attr == 'line-space':
                                 scale = self.fontsize * 2.0
+                            
+                            if val == "":
+                                val = 0
 
                             if not ((attr == 'hang') and (int(val) == 0)) :
                                 pv = float(val)/scale
index 6afb7daf1c7af8d52f8a0839b9d31fdea3a87978..f1d8574d1bee52c60b39c5215703217336251e2b 100644 (file)
@@ -31,11 +31,8 @@ class TpzDRMError(Exception):
 # local support routines
 if inCalibre:
     from calibre_plugins.k4mobidedrm import kgenpids
-    from calibre_plugins.k4mobidedrm import genbook
 else:
     import kgenpids
-    import genbook
-
 
 # recursive zip creation support routine
 def zipUpDir(myzip, tdir, localname):
@@ -271,6 +268,11 @@ class TopazBook:
             self.createBookDirectory()
             self.extractFiles()
             print "Successfully Extracted Topaz contents"
+            if inCalibre:
+                from calibre_plugins.k4mobidedrm import genbook
+            else:
+                import genbook
+            
             rv = genbook.generateBook(self.outdir, raw, fixedimage)
             if rv == 0:
                 print "\nBook Successfully generated"
@@ -300,6 +302,11 @@ class TopazBook:
         self.createBookDirectory()
         self.extractFiles()
         print "Successfully Extracted Topaz contents"
+        if inCalibre:
+            from calibre_plugins.k4mobidedrm import genbook
+        else:
+            import genbook
+        
         rv = genbook.generateBook(self.outdir, raw, fixedimage)
         if rv == 0:
             print "\nBook Successfully generated"
index 035547edaf52e2310e6d5d6280b8116e3c2d8b1f..38356dd507d8d28fdd30652f606582ca5fe4ea46 100644 (file)
@@ -1,23 +1,31 @@
-eReader PDB2PML - eReaderPDB2PML_vXX_plugin.zip\r\r
-\r\r
-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.\r\r
-\r\r
-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.\r\r
-\r\r
-Installation:\r\r
-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.\r
-\r
-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.\r\r
-\r\r
-Configuration:\r\r
-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\r\r
-\r\r
-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!!)\r\r
-\r\r
-Troubleshooting:\r\r
-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\r\r
-as well get used to it. ;)\r\r
-\r\r
-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.\r\r
-\r\r
-** 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.\r\r
+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.
+
+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.
+
+
+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:
+
+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
+
+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!!)
+
+
+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.
+
+
index 83dd0aee79726e60f7181653f746c159f25e49b9..81389b913e4a6fda12125a4d66fd7a9f5f788de3 100644 (file)
Binary files a/Calibre_Plugins/ineptpdf_plugin.zip and b/Calibre_Plugins/ineptpdf_plugin.zip differ
index e1b6041cec94ef78edbf6819708c0ce840645089..c76e332565bdef9880567a6f42d8fdc8073e45e8 100644 (file)
@@ -1,6 +1,8 @@
 #! /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
 # later.  <http://www.gnu.org/licenses/>
 
 #   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.4 - update to the new calibre plugin interface
-
+#   0.1.5 - synced to ineptpdf 7.11
 """
 Decrypts Adobe ADEPT-encrypted PDF files.
 """
 
-from __future__ import with_statement
-
 __license__ = 'GPL v3'
 
 import sys
@@ -116,13 +116,13 @@ def _load_crypto_libcrypto():
     class RSA(Structure):
         pass
     RSA_p = POINTER(RSA)
-    
+
     def F(restype, name, argtypes):
         func = getattr(libcrypto, name)
         func.restype = restype
         func.argtypes = argtypes
         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_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))
             if rsa is None:
                 raise ADEPTError('Error parsing ADEPT user key DER')
-        
+
         def decrypt(self, from_):
             rsa = self._rsa
             to = create_string_buffer(RSA_size(rsa))
@@ -152,7 +152,7 @@ def _load_crypto_libcrypto():
             if dlen < 0:
                 raise ADEPTError('RSA decryption failed')
             return to[1:dlen]
-    
+
         def __del__(self):
             if self._rsa is not None:
                 RSA_free(self._rsa)
@@ -214,13 +214,13 @@ def _load_crypto_pycrypto():
     # ASN.1 parsing code from tlslite
     class ASN1Error(Exception):
         pass
-    
+
     class ASN1Parser(object):
         class Parser(object):
             def __init__(self, bytes):
                 self.bytes = bytes
                 self.index = 0
-    
+
             def get(self, length):
                 if self.index + length > len(self.bytes):
                     raise ASN1Error("Error decoding ASN.1")
@@ -230,22 +230,22 @@ def _load_crypto_pycrypto():
                     x |= self.bytes[self.index]
                     self.index += 1
                 return x
-    
+
             def getFixBytes(self, lengthBytes):
                 bytes = self.bytes[self.index : self.index+lengthBytes]
                 self.index += lengthBytes
                 return bytes
-    
+
             def getVarBytes(self, lengthLength):
                 lengthBytes = self.get(lengthLength)
                 return self.getFixBytes(lengthBytes)
-    
+
             def getFixList(self, length, lengthList):
                 l = [0] * lengthList
                 for x in range(lengthList):
                     l[x] = self.get(length)
                 return l
-    
+
             def getVarList(self, length, lengthLength):
                 lengthList = self.get(lengthLength)
                 if lengthList % length != 0:
@@ -255,19 +255,19 @@ def _load_crypto_pycrypto():
                 for x in range(lengthList):
                     l[x] = self.get(length)
                 return l
-    
+
             def startLengthCheck(self, lengthLength):
                 self.lengthCheck = self.get(lengthLength)
                 self.indexCheck = self.index
-    
+
             def setLengthCheck(self, length):
                 self.lengthCheck = length
                 self.indexCheck = self.index
-    
+
             def stopLengthCheck(self):
                 if (self.index - self.indexCheck) != self.lengthCheck:
                     raise ASN1Error("Error decoding ASN.1")
-    
+
             def atLengthCheck(self):
                 if (self.index - self.indexCheck) < self.lengthCheck:
                     return False
@@ -275,13 +275,13 @@ def _load_crypto_pycrypto():
                     return True
                 else:
                     raise ASN1Error("Error decoding ASN.1")
-    
+
         def __init__(self, bytes):
             p = self.Parser(bytes)
             p.get(1)
             self.length = self._getASN1Length(p)
             self.value = p.getFixBytes(self.length)
-    
+
         def getChild(self, which):
             p = self.Parser(self.value)
             for x in range(which+1):
@@ -290,7 +290,7 @@ def _load_crypto_pycrypto():
                 length = self._getASN1Length(p)
                 p.getFixBytes(length)
             return ASN1Parser(p.bytes[markIndex:p.index])
-    
+
         def _getASN1Length(self, p):
             firstLength = p.get(1)
             if firstLength<=127:
@@ -311,6 +311,7 @@ def _load_crypto_pycrypto():
             return self._arc4.decrypt(data)
 
     class AES(object):
+        MODE_CBC = _AES.MODE_CBC
         @classmethod
         def new(cls, userkey, mode, iv):
             self = AES()
@@ -333,7 +334,7 @@ def _load_crypto_pycrypto():
             for byte in bytes:
                 total = (total << 8) + byte
             return total
-    
+
         def decrypt(self, data):
             return self._rsa.decrypt(data)
 
@@ -426,7 +427,7 @@ class PSLiteral(PSObject):
     def __init__(self, name):
         self.name = name
         return
-    
+
     def __repr__(self):
         name = []
         for char in self.name:
@@ -445,22 +446,22 @@ class PSKeyword(PSObject):
     def __init__(self, name):
         self.name = name
         return
-    
+
     def __repr__(self):
         return self.name
 
 # PSSymbolTable
 class PSSymbolTable(object):
-    
+
     '''
     Symbol table that stores PSLiteral or PSKeyword.
     '''
-    
+
     def __init__(self, classe):
         self.dic = {}
         self.classe = classe
         return
-    
+
     def intern(self, name):
         if name in self.dic:
             lit = self.dic[name]
@@ -530,11 +531,11 @@ class PSBaseParser(object):
 
     def flush(self):
         return
-    
+
     def close(self):
         self.flush()
         return
-    
+
     def tell(self):
         return self.bufpos+self.charpos
 
@@ -570,7 +571,7 @@ class PSBaseParser(object):
             raise PSEOF('Unexpected EOF')
         self.charpos = 0
         return
-    
+
     def parse_main(self, s, i):
         m = NONSPC.search(s, i)
         if not m:
@@ -605,11 +606,11 @@ class PSBaseParser(object):
             return (self.parse_wclose, j+1)
         self.add_token(KWD(c))
         return (self.parse_main, j+1)
-                            
+
     def add_token(self, obj):
         self.tokens.append((self.tokenstart, obj))
         return
-    
+
     def parse_comment(self, s, i):
         m = EOL.search(s, i)
         if not m:
@@ -620,7 +621,7 @@ class PSBaseParser(object):
         # We ignore comments.
         #self.tokens.append(self.token)
         return (self.parse_main, j)
-    
+
     def parse_literal(self, s, i):
         m = END_LITERAL.search(s, i)
         if not m:
@@ -634,7 +635,7 @@ class PSBaseParser(object):
             return (self.parse_literal_hex, j+1)
         self.add_token(LIT(self.token))
         return (self.parse_main, j)
-    
+
     def parse_literal_hex(self, s, i):
         c = s[i]
         if HEX.match(c) and len(self.hex) < 2:
@@ -669,7 +670,7 @@ class PSBaseParser(object):
         self.token += s[i:j]
         self.add_token(float(self.token))
         return (self.parse_main, j)
-    
+
     def parse_keyword(self, s, i):
         m = END_KEYWORD.search(s, i)
         if not m:
@@ -817,7 +818,7 @@ class PSStackParser(PSBaseParser):
         PSBaseParser.__init__(self, fp)
         self.reset()
         return
-    
+
     def reset(self):
         self.context = []
         self.curtype = None
@@ -858,10 +859,10 @@ class PSStackParser(PSBaseParser):
 
     def do_keyword(self, pos, token):
         return
-    
+
     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
         are represented as Python sequence and dictionaries.
         '''
@@ -930,7 +931,7 @@ class PDFNotImplementedError(PSException): pass
 ##  PDFObjRef
 ##
 class PDFObjRef(PDFObject):
-    
+
     def __init__(self, doc, objid, genno):
         if objid == 0:
             if STRICT:
@@ -1045,25 +1046,25 @@ def stream_value(x):
 
 # ascii85decode(data)
 def ascii85decode(data):
-  n = b = 0
-  out = ''
-  for c in data:
-    if '!' <= c and c <= 'u':
-      n += 1
-      b = b*85+(ord(c)-33)
-      if n == 5:
-        out += struct.pack('>L',b)
-        n = b = 0
-    elif c == 'z':
-      assert n == 0
-      out += '\0\0\0\0'
-    elif c == '~':
-      if n:
-        for _ in range(5-n):
-          b = b*85+84
-        out += struct.pack('>L',b)[:n-1]
-      break
-  return out
+    n = b = 0
+    out = ''
+    for c in data:
+        if '!' <= c and c <= 'u':
+            n += 1
+            b = b*85+(ord(c)-33)
+            if n == 5:
+                out += struct.pack('>L',b)
+                n = b = 0
+        elif c == 'z':
+            assert n == 0
+            out += '\0\0\0\0'
+        elif c == '~':
+            if n:
+                for _ in range(5-n):
+                    b = b*85+84
+                out += struct.pack('>L',b)[:n-1]
+            break
+    return out
 
 
 ##  PDFStream type
@@ -1080,7 +1081,7 @@ class PDFStream(PDFObject):
         else:
             if eol in ('\r', '\n', '\r\n'):
                 rawdata = rawdata[:length]
-                
+
         self.dic = dic
         self.rawdata = rawdata
         self.decipher = decipher
@@ -1094,7 +1095,7 @@ class PDFStream(PDFObject):
         self.objid = objid
         self.genno = genno
         return
-    
+
     def __repr__(self):
         if self.rawdata:
             return '<PDFStream(%r): raw=%d, %r>' % \
@@ -1178,7 +1179,7 @@ class PDFStream(PDFObject):
             data = self.decipher(self.objid, self.genno, data)
         return data
 
-        
+
 ##  PDF Exceptions
 ##
 class PDFSyntaxError(PDFException): pass
@@ -1243,7 +1244,7 @@ class PDFXRef(object):
                 self.offsets[objid] = (int(genno), int(pos))
         self.load_trailer(parser)
         return
-    
+
     KEYWORD_TRAILER = PSKeywordTable.intern('trailer')
     def load_trailer(self, parser):
         try:
@@ -1284,7 +1285,7 @@ class PDFXRefStream(object):
         for first, size in self.index:
             for objid in xrange(first, first + size):
                 yield objid
-    
+
     def load(self, parser, debug=0):
         (_,objid) = parser.nexttoken() # ignored
         (_,genno) = parser.nexttoken() # ignored
@@ -1302,7 +1303,7 @@ class PDFXRefStream(object):
         self.entlen = self.fl1+self.fl2+self.fl3
         self.trailer = stream.dic
         return
-    
+
     def getpos(self, objid):
         offset = 0
         for first, size in self.index:
@@ -1353,7 +1354,7 @@ class PDFDocument(object):
         self.parser = parser
         # The document is set to be temporarily ready during collecting
         # 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.
         self.ready = True
         # Retrieve the information of each header that was appended
@@ -1429,7 +1430,7 @@ class PDFDocument(object):
         length = int_value(param.get('Length', 0)) / 8
         edcdata = str_value(param.get('EDCData')).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 = []
         for pair in edcdata.split('\n'):
             edclist.append(pair)
@@ -1449,9 +1450,9 @@ class PDFDocument(object):
             raise ADEPTError('Could not decrypt PDRLPol, aborting ...')
         else:
             cutter = -1 * ord(pdrlpol[-1])
-            pdrlpol = pdrlpol[:cutter]            
+            pdrlpol = pdrlpol[:cutter]
         return plaintext[:16]
-    
+
     PASSWORD_PADDING = '(\xbfN^Nu\x8aAd\x00NV\xff\xfa\x01\x08..' \
                        '\x00\xb6\xd0h>\x80/\x0c\xa9\xfedSiz'
     # experimental aes pw support
@@ -1471,14 +1472,14 @@ class PDFDocument(object):
             EncMetadata = str_value(param['EncryptMetadata'])
         except:
             EncMetadata = 'True'
-        self.is_printable = bool(P & 4)        
+        self.is_printable = bool(P & 4)
         self.is_modifiable = bool(P & 8)
         self.is_extractable = bool(P & 16)
         self.is_annotationable = bool(P & 32)
         self.is_formsenabled = bool(P & 256)
         self.is_textextractable = bool(P & 512)
         self.is_assemblable = bool(P & 1024)
-        self.is_formprintable = bool(P & 2048) 
+        self.is_formprintable = bool(P & 2048)
         # Algorithm 3.2
         password = (password+self.PASSWORD_PADDING)[:32] # 1
         hash = hashlib.md5(password) # 2
@@ -1587,7 +1588,7 @@ class PDFDocument(object):
         hash = hashlib.md5(key)
         key = hash.digest()[:min(len(self.decrypt_key) + 5, 16)]
         return key
-    
+
     def genkey_v3(self, objid, genno):
         objid = struct.pack('<L', objid ^ 0x3569ac)
         genno = struct.pack('<L', genno ^ 0xca96)
@@ -1627,14 +1628,14 @@ class PDFDocument(object):
         #print cutter
         plaintext = plaintext[:cutter]
         return plaintext
-    
+
     def decrypt_rc4(self, objid, genno, data):
         key = self.genkey(objid, genno)
         return ARC4.new(key).decrypt(data)
 
 
     KEYWORD_OBJ = PSKeywordTable.intern('obj')
-    
+
     def getobj(self, objid):
         if not self.ready:
             raise PDFException('PDFDocument not initialized')
@@ -1704,7 +1705,7 @@ class PDFDocument(object):
 ##                    if x:
 ##                        objid1 = x[-2]
 ##                        genno = x[-1]
-##                
+##
                 if kwd is not self.KEYWORD_OBJ:
                     raise PDFSyntaxError(
                         'Invalid object spec: offset=%r' % index)
@@ -1716,7 +1717,7 @@ class PDFDocument(object):
             self.objs[objid] = obj
         return obj
 
-                
+
 class PDFObjStmRef(object):
     maxindex = 0
     def __init__(self, objid, stmid, index):
@@ -1726,7 +1727,7 @@ class PDFObjStmRef(object):
         if index > PDFObjStmRef.maxindex:
             PDFObjStmRef.maxindex = index
 
-    
+
 ##  PDFParser
 ##
 class PDFParser(PSStackParser):
@@ -1752,7 +1753,7 @@ class PDFParser(PSStackParser):
         if token is self.KEYWORD_ENDOBJ:
             self.add_results(*self.pop(4))
             return
-        
+
         if token is self.KEYWORD_R:
             # reference to indirect object
             try:
@@ -1763,7 +1764,7 @@ class PDFParser(PSStackParser):
             except PSSyntaxError:
                 pass
             return
-            
+
         if token is self.KEYWORD_STREAM:
             # stream object
             ((_,dic),) = self.pop(1)
@@ -1803,7 +1804,7 @@ class PDFParser(PSStackParser):
             obj = PDFStream(dic, data, self.doc.decipher)
             self.push((pos, obj))
             return
-        
+
         # others
         self.push((pos, token))
         return
@@ -1839,7 +1840,7 @@ class PDFParser(PSStackParser):
             xref.load(self)
         else:
             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))
             self.nextline()
             xref = PDFXRef()
@@ -1854,7 +1855,7 @@ class PDFParser(PSStackParser):
             pos = int_value(trailer['Prev'])
             self.read_xref_from(pos, xrefs)
         return
-        
+
     # read xref tables and trailers
     def read_xref(self):
         xrefs = []
@@ -1973,7 +1974,7 @@ class PDFSerializer(object):
                     self.write("%010d 00000 n \n" % xrefs[objid][0])
                 else:
                     self.write("%010d %05d f \n" % (0, 65535))
-            
+
             self.write('trailer\n')
             self.serialize_object(trailer)
             self.write('\nstartxref\n%d\n%%%%EOF' % startxref)
@@ -1993,7 +1994,7 @@ class PDFSerializer(object):
             while maxindex >= power:
                 fl3 += 1
                 power *= 256
-                    
+
             index = []
             first = None
             prev = None
@@ -2020,14 +2021,14 @@ class PDFSerializer(object):
                     # we force all generation numbers to be 0
                     # f3 = objref[1]
                     f3 = 0
-                
+
                 data.append(struct.pack('>B', f1))
                 data.append(struct.pack('>L', f2)[-fl2:])
                 data.append(struct.pack('>L', f3)[-fl3:])
             index.extend((first, prev - first + 1))
             data = zlib.compress(''.join(data))
             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],
                    'Root': trailer['Root'],}
             if 'Info' in trailer:
@@ -2049,9 +2050,9 @@ class PDFSerializer(object):
         string = string.replace(')', r'\)')
          # get rid of ciando id
         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
-    
+
     def serialize_object(self, obj):
         if isinstance(obj, dict):
             # Correct malformed Mac OS resource forks for Stanza
@@ -2075,21 +2076,21 @@ class PDFSerializer(object):
         elif isinstance(obj, bool):
             if self.last.isalnum():
                 self.write(' ')
-            self.write(str(obj).lower())            
+            self.write(str(obj).lower())
         elif isinstance(obj, (int, long, float)):
             if self.last.isalnum():
                 self.write(' ')
             self.write(str(obj))
         elif isinstance(obj, PDFObjRef):
             if self.last.isalnum():
-                self.write(' ')            
+                self.write(' ')
             self.write('%d %d R' % (obj.objid, 0))
         elif isinstance(obj, PDFStream):
             ### If we don't generate cross ref streams the object streams
             ### are no longer useful, as we have extracted all objects from
             ### them. Therefore leave them out from the output.
             if obj.dic.get('Type') == LITERAL_OBJSTM and not gen_xref_stm:
-                    self.write('(deleted)')
+                self.write('(deleted)')
             else:
                 data = obj.get_decdata()
                 self.serialize_object(obj.dic)
@@ -2101,7 +2102,7 @@ class PDFSerializer(object):
             if data[0].isalnum() and self.last.isalnum():
                 self.write(' ')
             self.write(data)
-    
+
     def serialize_indirect(self, objid, obj):
         self.write('%d 0 obj' % (objid,))
         self.serialize_object(obj)
@@ -2136,7 +2137,7 @@ class IneptPDFDeDRM(FileTypePlugin):
                                 Credit given to I <3 Cabbages for the original stand-alone scripts.'
     supported_platforms     = ['linux', 'osx', 'windows']
     author                  = 'DiapDealer'
-    version                 = (0, 1, 4)
+    version                 = (0, 1, 5)
     minimum_calibre_version = (0, 7, 55)  # for the new plugin interface
     file_types              = set(['pdf'])
     on_import               = True
index 781a22d053be611068558bda16ebbc629ff914c9..59318c25e547c3cdd5fbf96a5ac90df763e24478 100644 (file)
Binary files a/Calibre_Plugins/k4mobidedrm_plugin.zip and b/Calibre_Plugins/k4mobidedrm_plugin.zip differ
index e66e9f3c76ec750d283d6dc12542011256a85db9..e51b094ef89dc4c0522b0e7d2d5c4951812d696f 100644 (file)
@@ -385,17 +385,22 @@ def GetIDString():
     if isNewInstall():
         mungedmac = GetMACAddressMunged()
         if len(mungedmac) > 7:
+            print('Using Munged MAC Address for ID: '+mungedmac)
             return mungedmac
     sernum = GetVolumeSerialNumber()
     if len(sernum) > 7:
+        print('Using Volume Serial Number for ID: '+sernum)
         return sernum
     diskpart = GetUserHomeAppSupKindleDirParitionName()
     uuidnum = GetDiskPartitionUUID(diskpart)
     if len(uuidnum) > 7:
+        print('Using Disk Partition UUID for ID: '+uuidnum)
         return uuidnum
     mungedmac = GetMACAddressMunged()
     if len(mungedmac) > 7:
+        print('Using Munged MAC Address for ID: '+mungedmac)
         return mungedmac
+    print('Using Fixed constant 9999999999 for ID.')
     return '9999999999'
 
 
@@ -498,6 +503,7 @@ def getKindleInfoFiles(kInfoFiles):
     for resline in reslst:
         if os.path.isfile(resline):
             kInfoFiles.append(resline)
+            print('Found K4Mac kindle-info file: ' + resline)
             found = True
     # add any .rainier*-kinf files
     cmdline = 'find "' + home + '/Library/Application Support" -name ".rainier*-kinf"'
@@ -508,6 +514,7 @@ def getKindleInfoFiles(kInfoFiles):
     for resline in reslst:
         if os.path.isfile(resline):
             kInfoFiles.append(resline)
+            print('Found k4Mac kinf file: ' + resline)
             found = True
     # add any .kinf2011 files
     cmdline = 'find "' + home + '/Library/Application Support" -name ".kinf2011"'
@@ -518,9 +525,10 @@ def getKindleInfoFiles(kInfoFiles):
     for resline in reslst:
         if os.path.isfile(resline):
             kInfoFiles.append(resline)
+            print('Found k4Mac kinf2011 file: ' + resline)
             found = True
     if not found:
-        print('No kindle-info files have been found.')
+        print('No k4Mac kindle-info/kinf/kinf2011 files have been found.')
     return kInfoFiles
 
 # determine type of kindle info provided and return a
index 88314f67c1bf33711c873f976a56bbd4a8096e43..1bd256234dfd4159fa15113db0efa2993ee80e5d 100644 (file)
@@ -151,7 +151,9 @@ def GetVolumeSerialNumber():
 GetVolumeSerialNumber = GetVolumeSerialNumber()
 
 def GetIDString():
-    return GetVolumeSerialNumber()
+    vsn = GetVolumeSerialNumber()
+    print('Using Volume Serial Number for ID: '+vsn)
+    return vsn
 
 def getLastError():
     GetLastError = kernel32.GetLastError
@@ -210,37 +212,40 @@ def getKindleInfoFiles(kInfoFiles):
     if 'LOCALAPPDATA' in os.environ.keys():
         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
     kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info'
-    if not os.path.isfile(kinfopath):
-        print('No kindle.info files have not been found.')
-    else:
+    if os.path.isfile(kinfopath):
+        found = True
+        print('Found K4PC kindle.info file: ' + kinfopath)
         kInfoFiles.append(kinfopath)
 
     # 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'
-    if not os.path.isfile(kinfopath):
-        print('No K4PC 1.5.X .kinf files have not been found.')
-    else:
+    if os.path.isfile(kinfopath):
+        found = True
+        print('Found K4PC 1.5.X kinf file: ' + kinfopath)
         kInfoFiles.append(kinfopath)
 
     # 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'
-    if not os.path.isfile(kinfopath):
-        print('No K4PC 1.6.X .kinf files have not been found.')
-    else:
+    if os.path.isfile(kinfopath):
+        found = True
+        print('Found K4PC 1.6.X kinf file: ' + kinfopath)
         kInfoFiles.append(kinfopath)
 
     # now look for even newer (K4PC 1.9.0 and later) .kinf2011 file
     kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2011'
-    if not os.path.isfile(kinfopath):
-        print('No K4PC 1.9.X .kinf files have not been found.')
-    else:
+    if os.path.isfile(kinfopath):
+        found = True
+        print('Found K4PC kinf2011 file: ' + kinfopath)
         kInfoFiles.append(kinfopath)
 
+    if not found:
+        print('No K4PC kindle.info/kinf/kinf2011 files have been found.')
     return kInfoFiles
 
 
index b6275d48a0d3ea58037aada8a72194db730b9d02..1ad2bacca76e28c4a5197e245bc203dcec390f5b 100644 (file)
 #  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.35'
+
+__version__ = '0.37'
 
 import sys
 
@@ -168,9 +171,10 @@ class MobiBook:
         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())
+    def __init__(self, infile, announce = True):
+        if announce:
+            print ('MobiDeDrm v%(__version__)s. '
+               'Copyright 2008-2012 The Dark Reverser et al.' % globals())
 
         # initial sanity check on file
         self.data_file = file(infile, 'rb').read()
@@ -198,6 +202,7 @@ class MobiBook:
             print "Book has format: ", self.magic
             self.extra_data_flags = 0
             self.mobi_length = 0
+            self.mobi_codepage = 1252
             self.mobi_version = -1
             self.meta_array = {}
             return
@@ -248,18 +253,19 @@ class MobiBook:
             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]
+        codec = 'windows-1252'
+        if self.magic == 'BOOKMOBI':
+            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 self.mobi_codepage in codec_map.keys():
+                codec = codec_map[self.mobi_codepage]
         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):
@@ -375,7 +381,7 @@ class MobiBook:
                 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. 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
             self.patchSection(0, "\0" * drm_size, drm_ptr)
             # kill the drm pointers
@@ -411,26 +417,26 @@ class MobiBook:
         print "done"
         return
 
-def getUnencryptedBook(infile,pid):
+def getUnencryptedBook(infile,pid,announce=True):
     if not os.path.isfile(infile):
         raise DrmException('Input File Not Found')
-    book = MobiBook(infile)
+    book = MobiBook(infile,announce)
     book.processBook([pid])
     return book.mobi_data
 
-def getUnencryptedBookWithList(infile,pidlist):
+def getUnencryptedBookWithList(infile,pidlist,announce=True):
     if not os.path.isfile(infile):
         raise DrmException('Input File Not Found')
-    book = MobiBook(infile)
+    book = MobiBook(infile, announce)
     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())
+        'Copyright 2008-2012 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 "Removes protection from Kindle/Mobipocket, Kindle/KF8 and Kindle/Print Replica ebooks"
         print "Usage:"
         print "    %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0]
         return 1
@@ -442,7 +448,7 @@ def main(argv=sys.argv):
         else:
             pidlist = {}
         try:
-            stripped_file = getUnencryptedBookWithList(infile, pidlist)
+            stripped_file = getUnencryptedBookWithList(infile, pidlist, False)
             file(outfile, 'wb').write(stripped_file)
         except DrmException, e:
             print "Error: %s" % e
index 062253c5132c6af1c6365bf0d7c60d43a1cd6f0e..61c74bf61c79d43c1da933fdea5b372ff15e52f6 100644 (file)
@@ -1,49 +1,46 @@
 {\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf360
 {\fonttbl\f0\fswiss\fcharset0 Helvetica;}
 {\colortbl;\red255\green255\blue255;}
-\paperw11900\paperh16840\margl1440\margr1440\vieww10320\viewh9840\viewkind0
-\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\ql\qnatural\pardirnatural
+\paperw11900\paperh16840\margl1440\margr1440\vieww12360\viewh16560\viewkind0
+\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 \
-\
+\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\ql\qnatural\pardirnatural
+\cf0 \
 \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.\
+\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.\
 \
-It should work 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:\
 \
-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.\
+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\
 \
-Once these preferences have been set, the user can simply drag and drop ebooks onto the DeDRM droplet to remove the DRM.\
+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.\
 \
-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
-\cf0 \
+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 \
-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
+\b0 \
+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.\
 \
 \
 
-\b Use\
+\b Troubleshooting\
 
-\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.\
-\
-2. Drag & Drop DRMed ebooks or folders containing DRMed ebooks onto the Application, either in your Applications folder, or the icon in your dock.}
\ No newline at end of file
+\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.}
\ No newline at end of file
index 2d01000a4dff616d0e8cb550c3c895ee3fa6819d..35f0528bd6e1a30aa3c824340284ad873d24a96f 100644 (file)
Binary files a/DeDRM_Macintosh_Application/DeDRM.app.txt and b/DeDRM_Macintosh_Application/DeDRM.app.txt differ
index 79ad5dff571cae1b452c7f7c80eda02f0ce05b00..960cd2942b0ac8bc5f5b7ebb7729ee8e295f841e 100644 (file)
        <key>CFBundleExecutable</key>
        <string>droplet</string>
        <key>CFBundleGetInfoString</key>
-       <string>DeDRM 5.1, Written 2010–2012 by Apprentice Alf and others.</string>
+       <string>DeDRM 5.2, Written 2010–2012 by Apprentice Alf and others.</string>
        <key>CFBundleIconFile</key>
-       <string>droplet</string>
+       <string>DeDRM</string>
        <key>CFBundleInfoDictionaryVersion</key>
        <string>6.0</string>
        <key>CFBundleName</key>
-       <string>DeDRM 5.1</string>
+       <string>DeDRM 5.2</string>
        <key>CFBundlePackageType</key>
        <string>APPL</string>
        <key>CFBundleShortVersionString</key>
-       <string>5.1</string>
+       <string>5.2</string>
        <key>CFBundleSignature</key>
        <string>dplt</string>
        <key>LSMinimumSystemVersion</key>
        <true/>
        <key>WindowState</key>
        <dict>
-               <key>dividerCollapsed</key>
-               <false/>
-               <key>eventLogLevel</key>
-               <integer>-1</integer>
                <key>name</key>
                <string>ScriptWindowState</string>
                <key>positionOfDivider</key>
-               <real>460</real>
+               <real>554</real>
                <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>
                <string>event log</string>
        </dict>
index 81eb3a300ae04dd527ba7db1110383b0f15e9529..39367189aa3c841c692e4b21b7ee93ee1d285728 100644 (file)
Binary files a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/DeDRM Progress Source.zip and b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/DeDRM Progress Source.zip differ
index ceaa16ffccf893924bd27bf743a867e295a009e1..a22c653b197911a1b17f7dbed99dbf5b84ef1459 100644 (file)
@@ -2,10 +2,16 @@
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
+       <key>BuildMachineOSBuild</key>
+       <string>10K549</string>
        <key>CFBundleDevelopmentRegion</key>
        <string>English</string>
        <key>CFBundleExecutable</key>
        <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>
        <string>com.apprenticealf.DeDRMProgress</string>
        <key>CFBundleInfoDictionaryVersion</key>
        <key>CFBundlePackageType</key>
        <string>APPL</string>
        <key>CFBundleShortVersionString</key>
-       <string>1.0</string>
+       <string>1.1</string>
        <key>CFBundleSignature</key>
        <string>????</string>
        <key>CFBundleVersion</key>
        <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>
        <string>YES</string>
        <key>NSMainNibFile</key>
index 2ff942b9964ae48bae3e6b6adf0fbd35664742ed..01afac090e89c43f4d574172b7c47d56bfab7162 100644 (file)
Binary files a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/DeDRM Progress.app/Contents/MacOS/DeDRM Progress and b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/DeDRM Progress.app/Contents/MacOS/DeDRM Progress differ
diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/DeDRM Progress.app/Contents/Resources/DeDRM Progress.icns b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/DeDRM Progress.app/Contents/Resources/DeDRM Progress.icns
new file mode 100644 (file)
index 0000000..69855fc
Binary files /dev/null and b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/DeDRM Progress.app/Contents/Resources/DeDRM Progress.icns differ
diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/DeDRM Progress.app/Contents/Resources/English.lproj/MainMenu.nib b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/DeDRM Progress.app/Contents/Resources/English.lproj/MainMenu.nib
new file mode 100644 (file)
index 0000000..83ceb9e
Binary files /dev/null and b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/DeDRM Progress.app/Contents/Resources/English.lproj/MainMenu.nib differ
diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/DeDRM.icns b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/DeDRM.icns
new file mode 100644 (file)
index 0000000..8a6dfe3
Binary files /dev/null and b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/DeDRM.icns differ
index 3a57c3ba4bd6007054d4a8a3698f501e0490c463..aa5c55acea5ca150c521d98f13c52167266b779b 100644 (file)
Binary files a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/Scripts/main.scpt and b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/Scripts/main.scpt differ
diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/alfcrypto.dll b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/alfcrypto.dll
new file mode 100644 (file)
index 0000000..26d740d
Binary files /dev/null and b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/alfcrypto.dll differ
diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/alfcrypto64.dll b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/alfcrypto64.dll
new file mode 100644 (file)
index 0000000..7bef68e
Binary files /dev/null and b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/alfcrypto64.dll differ
diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/cmbtc_v2.2.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/cmbtc_v2.2.py
new file mode 100644 (file)
index 0000000..7be7a8a
--- /dev/null
@@ -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())
diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/config.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/config.py
new file mode 100644 (file)
index 0000000..c029760
--- /dev/null
@@ -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
index 5312a38b2d2be6f4fdff8ac2cfe68b16c3960520..c412d7b1ba2e38eb8c61ab119fd2b2877cccde2a 100644 (file)
@@ -214,6 +214,7 @@ class PageParser(object):
         'links.title'  : (1, 'text', 0, 0),
         'links.href'   : (1, 'text', 0, 0),
         'links.type'   : (1, 'text', 0, 0),
+        'links.id'     : (1, 'number', 0, 0),
 
         'paraCont'          : (0, 'number', 1, 1),
         'paraCont.rootID'   : (1, 'number', 0, 0),
@@ -239,6 +240,7 @@ class PageParser(object):
         'group'           : (1, 'snippets', 1, 0),
         'group.type'      : (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.type'      : (1, 'scalar_text', 0, 0),
@@ -246,7 +248,7 @@ class PageParser(object):
         'region.y'         : (1, 'scalar_number', 0, 0),
         'region.h'         : (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),
 
index 3cb2cea58312d350196e96ded923419960a5aa75..5554a802b8235703854f3ae30714817d24020c80 100644 (file)
Binary files a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/droplet.rsrc and b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/droplet.rsrc differ
diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/getk4pcpids.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/getk4pcpids.py
new file mode 100644 (file)
index 0000000..c4716bd
--- /dev/null
@@ -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())
index f710ea06a0efcf3dfefc31c30d1ce2bddd7adab1..20721d170461b84b8a41f25c6f98485b2810bf4e 100644 (file)
@@ -1,5 +1,5 @@
 #! /usr/bin/env python
-# ineptpdf.pyw, version 7.9
+# ineptpdf.pyw, version 7.11
 
 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.8 - Modify interface to allow use of import
 #   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.
@@ -293,6 +295,7 @@ def _load_crypto_pycrypto():
             return self._arc4.decrypt(data)
 
     class AES(object):
+        MODE_CBC = _AES.MODE_CBC
         @classmethod
         def new(cls, userkey, mode, iv):
             self = AES()
@@ -2199,11 +2202,19 @@ class DecryptionDialog(Tkinter.Frame):
 
 def decryptBook(keypath, inpath, outpath):
     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
         with open(outpath, 'wb') as outf:
         # 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
 
 
index a889564b8a4b2c8658243bf5eb5e03d2610ea0d2..4056690ade25589e64b2256d3f197248b399401f 100644 (file)
@@ -17,7 +17,7 @@ from __future__ import with_statement
 #    and many many others
 
 
-__version__ = '4.2'
+__version__ = '4.4'
 
 class Unbuffered:
     def __init__(self, stream):
@@ -58,7 +58,7 @@ else:
 # borrowed from calibre from calibre/src/calibre/__init__.py
 # added in removal of non-printing chars
 # 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):
     _filename_sanitize = re.compile(r'[\xae\0\\|\?\*<":>\+/]')
     substitute='_'
@@ -73,7 +73,7 @@ def cleanup_name(name):
     # Mac and Unix don't like file names that begin with a full stop
     if len(one) > 0 and one[0] == '.':
         one = substitute+one[1:]
-    one = one.replace(' ','_')
+    one = one.replace('_',' ')
     return one
 
 def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
@@ -99,11 +99,20 @@ def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
     title = mb.getBookTitle()
     print "Processing Book: ", title
     filenametitle = cleanup_name(title)
-    outfilename = bookname
-    if len(outfilename)<=8 or len(filenametitle)<=8:
-        outfilename = outfilename + "_" + filenametitle
-    elif outfilename[:8] != filenametitle[:8]:
-        outfilename = outfilename[:8] + "_" + filenametitle
+    outfilename = cleanup_name(bookname)
+    
+    # generate 'sensible' filename, that will sort with the original name,
+    # but is close to the name from the file.
+    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
     if len(outfilename)>150:
index e66e9f3c76ec750d283d6dc12542011256a85db9..e51b094ef89dc4c0522b0e7d2d5c4951812d696f 100644 (file)
@@ -385,17 +385,22 @@ def GetIDString():
     if isNewInstall():
         mungedmac = GetMACAddressMunged()
         if len(mungedmac) > 7:
+            print('Using Munged MAC Address for ID: '+mungedmac)
             return mungedmac
     sernum = GetVolumeSerialNumber()
     if len(sernum) > 7:
+        print('Using Volume Serial Number for ID: '+sernum)
         return sernum
     diskpart = GetUserHomeAppSupKindleDirParitionName()
     uuidnum = GetDiskPartitionUUID(diskpart)
     if len(uuidnum) > 7:
+        print('Using Disk Partition UUID for ID: '+uuidnum)
         return uuidnum
     mungedmac = GetMACAddressMunged()
     if len(mungedmac) > 7:
+        print('Using Munged MAC Address for ID: '+mungedmac)
         return mungedmac
+    print('Using Fixed constant 9999999999 for ID.')
     return '9999999999'
 
 
@@ -498,6 +503,7 @@ def getKindleInfoFiles(kInfoFiles):
     for resline in reslst:
         if os.path.isfile(resline):
             kInfoFiles.append(resline)
+            print('Found K4Mac kindle-info file: ' + resline)
             found = True
     # add any .rainier*-kinf files
     cmdline = 'find "' + home + '/Library/Application Support" -name ".rainier*-kinf"'
@@ -508,6 +514,7 @@ def getKindleInfoFiles(kInfoFiles):
     for resline in reslst:
         if os.path.isfile(resline):
             kInfoFiles.append(resline)
+            print('Found k4Mac kinf file: ' + resline)
             found = True
     # add any .kinf2011 files
     cmdline = 'find "' + home + '/Library/Application Support" -name ".kinf2011"'
@@ -518,9 +525,10 @@ def getKindleInfoFiles(kInfoFiles):
     for resline in reslst:
         if os.path.isfile(resline):
             kInfoFiles.append(resline)
+            print('Found k4Mac kinf2011 file: ' + resline)
             found = True
     if not found:
-        print('No kindle-info files have been found.')
+        print('No k4Mac kindle-info/kinf/kinf2011 files have been found.')
     return kInfoFiles
 
 # determine type of kindle info provided and return a
index 88314f67c1bf33711c873f976a56bbd4a8096e43..1bd256234dfd4159fa15113db0efa2993ee80e5d 100644 (file)
@@ -151,7 +151,9 @@ def GetVolumeSerialNumber():
 GetVolumeSerialNumber = GetVolumeSerialNumber()
 
 def GetIDString():
-    return GetVolumeSerialNumber()
+    vsn = GetVolumeSerialNumber()
+    print('Using Volume Serial Number for ID: '+vsn)
+    return vsn
 
 def getLastError():
     GetLastError = kernel32.GetLastError
@@ -210,37 +212,40 @@ def getKindleInfoFiles(kInfoFiles):
     if 'LOCALAPPDATA' in os.environ.keys():
         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
     kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info'
-    if not os.path.isfile(kinfopath):
-        print('No kindle.info files have not been found.')
-    else:
+    if os.path.isfile(kinfopath):
+        found = True
+        print('Found K4PC kindle.info file: ' + kinfopath)
         kInfoFiles.append(kinfopath)
 
     # 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'
-    if not os.path.isfile(kinfopath):
-        print('No K4PC 1.5.X .kinf files have not been found.')
-    else:
+    if os.path.isfile(kinfopath):
+        found = True
+        print('Found K4PC 1.5.X kinf file: ' + kinfopath)
         kInfoFiles.append(kinfopath)
 
     # 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'
-    if not os.path.isfile(kinfopath):
-        print('No K4PC 1.6.X .kinf files have not been found.')
-    else:
+    if os.path.isfile(kinfopath):
+        found = True
+        print('Found K4PC 1.6.X kinf file: ' + kinfopath)
         kInfoFiles.append(kinfopath)
 
     # now look for even newer (K4PC 1.9.0 and later) .kinf2011 file
     kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2011'
-    if not os.path.isfile(kinfopath):
-        print('No K4PC 1.9.X .kinf files have not been found.')
-    else:
+    if os.path.isfile(kinfopath):
+        found = True
+        print('Found K4PC kinf2011 file: ' + kinfopath)
         kInfoFiles.append(kinfopath)
 
+    if not found:
+        print('No K4PC kindle.info/kinf/kinf2011 files have been found.')
     return kInfoFiles
 
 
index c4e45be1daedaaa1a7ca1a0af4da6c2dc1745202..40d84ad713c427faa5b25b18a9a4a2cdcf100a6e 100644 (file)
@@ -262,9 +262,15 @@ def getPidList(md1, md2, k4, pids, serials, kInfoFiles):
     if k4:
         kInfoFiles = getKindleInfoFiles(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:
-        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:
         pidlst.append(pid)
     return pidlst
diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/kindlepid.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/kindlepid.py
new file mode 100644 (file)
index 0000000..90a59ad
--- /dev/null
@@ -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())
diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/libalfcrypto32.so b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/libalfcrypto32.so
new file mode 100644 (file)
index 0000000..9a5a442
Binary files /dev/null and b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/libalfcrypto32.so differ
diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/libalfcrypto64.so b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/libalfcrypto64.so
new file mode 100644 (file)
index 0000000..a08ac28
Binary files /dev/null and b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/libalfcrypto64.so differ
index b6275d48a0d3ea58037aada8a72194db730b9d02..1ad2bacca76e28c4a5197e245bc203dcec390f5b 100644 (file)
 #  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.35'
+
+__version__ = '0.37'
 
 import sys
 
@@ -168,9 +171,10 @@ class MobiBook:
         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())
+    def __init__(self, infile, announce = True):
+        if announce:
+            print ('MobiDeDrm v%(__version__)s. '
+               'Copyright 2008-2012 The Dark Reverser et al.' % globals())
 
         # initial sanity check on file
         self.data_file = file(infile, 'rb').read()
@@ -198,6 +202,7 @@ class MobiBook:
             print "Book has format: ", self.magic
             self.extra_data_flags = 0
             self.mobi_length = 0
+            self.mobi_codepage = 1252
             self.mobi_version = -1
             self.meta_array = {}
             return
@@ -248,18 +253,19 @@ class MobiBook:
             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]
+        codec = 'windows-1252'
+        if self.magic == 'BOOKMOBI':
+            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 self.mobi_codepage in codec_map.keys():
+                codec = codec_map[self.mobi_codepage]
         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):
@@ -375,7 +381,7 @@ class MobiBook:
                 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. 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
             self.patchSection(0, "\0" * drm_size, drm_ptr)
             # kill the drm pointers
@@ -411,26 +417,26 @@ class MobiBook:
         print "done"
         return
 
-def getUnencryptedBook(infile,pid):
+def getUnencryptedBook(infile,pid,announce=True):
     if not os.path.isfile(infile):
         raise DrmException('Input File Not Found')
-    book = MobiBook(infile)
+    book = MobiBook(infile,announce)
     book.processBook([pid])
     return book.mobi_data
 
-def getUnencryptedBookWithList(infile,pidlist):
+def getUnencryptedBookWithList(infile,pidlist,announce=True):
     if not os.path.isfile(infile):
         raise DrmException('Input File Not Found')
-    book = MobiBook(infile)
+    book = MobiBook(infile, announce)
     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())
+        'Copyright 2008-2012 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 "Removes protection from Kindle/Mobipocket, Kindle/KF8 and Kindle/Print Replica ebooks"
         print "Usage:"
         print "    %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0]
         return 1
@@ -442,7 +448,7 @@ def main(argv=sys.argv):
         else:
             pidlist = {}
         try:
-            stripped_file = getUnencryptedBookWithList(infile, pidlist)
+            stripped_file = getUnencryptedBookWithList(infile, pidlist, False)
             file(outfile, 'wb').write(stripped_file)
         except DrmException, e:
             print "Error: %s" % e
diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/mobidedrm_orig.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/mobidedrm_orig.py
deleted file mode 100644 (file)
index 9f0fad8..0000000
+++ /dev/null
@@ -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())
index adbac4988187e1dc4eb4ce403eb931039069ff6b..2347f6aeb128bd5cdfc22f8d52dc5d8c05ba63b2 100644 (file)
@@ -164,6 +164,9 @@ class DocParser(object):
                                 scale = self.pw
                             elif attr == 'line-space':
                                 scale = self.fontsize * 2.0
+                            
+                            if val == "":
+                                val = 0
 
                             if not ((attr == 'hang') and (int(val) == 0)) :
                                 pv = float(val)/scale
index 6afb7daf1c7af8d52f8a0839b9d31fdea3a87978..f1d8574d1bee52c60b39c5215703217336251e2b 100644 (file)
@@ -31,11 +31,8 @@ class TpzDRMError(Exception):
 # local support routines
 if inCalibre:
     from calibre_plugins.k4mobidedrm import kgenpids
-    from calibre_plugins.k4mobidedrm import genbook
 else:
     import kgenpids
-    import genbook
-
 
 # recursive zip creation support routine
 def zipUpDir(myzip, tdir, localname):
@@ -271,6 +268,11 @@ class TopazBook:
             self.createBookDirectory()
             self.extractFiles()
             print "Successfully Extracted Topaz contents"
+            if inCalibre:
+                from calibre_plugins.k4mobidedrm import genbook
+            else:
+                import genbook
+            
             rv = genbook.generateBook(self.outdir, raw, fixedimage)
             if rv == 0:
                 print "\nBook Successfully generated"
@@ -300,6 +302,11 @@ class TopazBook:
         self.createBookDirectory()
         self.extractFiles()
         print "Successfully Extracted Topaz contents"
+        if inCalibre:
+            from calibre_plugins.k4mobidedrm import genbook
+        else:
+            import genbook
+        
         rv = genbook.generateBook(self.outdir, raw, fixedimage)
         if rv == 0:
             print "\nBook Successfully generated"
index b8f5c6af6a15e6298d6d5bb3363c81bba799592e..4f87e620277afd4ef98ea2685f4615d37f82c9c9 100644 (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.
 
 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
-Barnes & Noble key files (bnepubkey.b64)
-eReader Social DRM: (Name:Last 8 digits of CC number)
-MobiPocket, Kindle for iPhone/iPad/iPodTouch  10 digit PID 
+eInk Kindle: 16 digit Serial Number
+Barnes & Noble: key file (bnepubkey.b64)
+eReader Social DRM: Name:Last 8 digits of CC number
+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.
 
 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
 ------------
 
-1. From tools_vX.X\DeDRM_Applications\, right click on DeDRM_v_X.X_WinApp.zip and fully Extract its contents. 
-
-2. Move the resulting DeDRM_vX.X_WinApp folder to whereever you keep you other programs.
-   (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.
+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.
 
-4. To set the preferences simply double-click on your just created short-cut.
+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.
 
+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!
 
index 418b3de5593f21ae5fa512a509553b58bf6af8b8..2e1abee40ca388f7f6ec8f2f5c9973d0e089084a 100644 (file)
@@ -21,7 +21,7 @@ import re
 import simpleprefs
 
 
-__version__ = '5.0'
+__version__ = '5.2'
 
 class DrmException(Exception):
     pass
@@ -38,7 +38,7 @@ class MainApp(Tk):
                         ['serials', 'seriallist.txt'],
                         ['sdrms'  , 'sdrmlist.txt'  ],
                         ['outdir' , 'outdir.txt'    ]]
-        self.po = simpleprefs.SimplePrefs('DeDRM',description)
+        self.po = simpleprefs.SimplePrefs("DeDRM",description)
         if self.dnd:
             self.cd = ConvDialog(self)
             prefs = self.getPreferences()
@@ -95,7 +95,7 @@ class PrefsDialog(Toplevel):
         Toplevel.__init__(self, mainapp)
         self.withdraw()
         self.protocol("WM_DELETE_WINDOW", self.withdraw)
-        self.title("DeDRM")
+        self.title("DeDRM " + __version__)
         self.prefs_array = prefs_array
         self.status = Tkinter.Label(self, text='Setting Preferences')
         self.status.pack(fill=Tkconstants.X, expand=1)
@@ -566,6 +566,7 @@ def main(argv=sys.argv):
         infilelst = argv[1:]
         filenames = []
         for infile in infilelst:
+            infile = infile.decode(sys.getfilesystemencoding())
             print infile
             infile = infile.replace('"','')
             infile = os.path.abspath(infile)
diff --git a/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/config.py b/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/config.py
new file mode 100644 (file)
index 0000000..c029760
--- /dev/null
@@ -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
index 5312a38b2d2be6f4fdff8ac2cfe68b16c3960520..c412d7b1ba2e38eb8c61ab119fd2b2877cccde2a 100644 (file)
@@ -214,6 +214,7 @@ class PageParser(object):
         'links.title'  : (1, 'text', 0, 0),
         'links.href'   : (1, 'text', 0, 0),
         'links.type'   : (1, 'text', 0, 0),
+        'links.id'     : (1, 'number', 0, 0),
 
         'paraCont'          : (0, 'number', 1, 1),
         'paraCont.rootID'   : (1, 'number', 0, 0),
@@ -239,6 +240,7 @@ class PageParser(object):
         'group'           : (1, 'snippets', 1, 0),
         'group.type'      : (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.type'      : (1, 'scalar_text', 0, 0),
@@ -246,7 +248,7 @@ class PageParser(object):
         'region.y'         : (1, 'scalar_number', 0, 0),
         'region.h'         : (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),
 
diff --git a/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/getk4pcpids.py b/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/getk4pcpids.py
new file mode 100644 (file)
index 0000000..c4716bd
--- /dev/null
@@ -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())
index 1437708372d3020386223b0069267af88bb1737d..20721d170461b84b8a41f25c6f98485b2810bf4e 100644 (file)
@@ -1,5 +1,5 @@
 #! /usr/bin/env python
-# ineptpdf.pyw, version 7.9
+# ineptpdf.pyw, version 7.11
 
 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.8 - Modify interface to allow use of import
 #   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.
@@ -2200,11 +2202,19 @@ class DecryptionDialog(Tkinter.Frame):
 
 def decryptBook(keypath, inpath, outpath):
     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
         with open(outpath, 'wb') as outf:
         # 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
 
 
index a889564b8a4b2c8658243bf5eb5e03d2610ea0d2..4056690ade25589e64b2256d3f197248b399401f 100644 (file)
@@ -17,7 +17,7 @@ from __future__ import with_statement
 #    and many many others
 
 
-__version__ = '4.2'
+__version__ = '4.4'
 
 class Unbuffered:
     def __init__(self, stream):
@@ -58,7 +58,7 @@ else:
 # borrowed from calibre from calibre/src/calibre/__init__.py
 # added in removal of non-printing chars
 # 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):
     _filename_sanitize = re.compile(r'[\xae\0\\|\?\*<":>\+/]')
     substitute='_'
@@ -73,7 +73,7 @@ def cleanup_name(name):
     # Mac and Unix don't like file names that begin with a full stop
     if len(one) > 0 and one[0] == '.':
         one = substitute+one[1:]
-    one = one.replace(' ','_')
+    one = one.replace('_',' ')
     return one
 
 def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
@@ -99,11 +99,20 @@ def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
     title = mb.getBookTitle()
     print "Processing Book: ", title
     filenametitle = cleanup_name(title)
-    outfilename = bookname
-    if len(outfilename)<=8 or len(filenametitle)<=8:
-        outfilename = outfilename + "_" + filenametitle
-    elif outfilename[:8] != filenametitle[:8]:
-        outfilename = outfilename[:8] + "_" + filenametitle
+    outfilename = cleanup_name(bookname)
+    
+    # generate 'sensible' filename, that will sort with the original name,
+    # but is close to the name from the file.
+    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
     if len(outfilename)>150:
index 69976541db3d7b0ee4a5f074d34b12c856033134..e51b094ef89dc4c0522b0e7d2d5c4951812d696f 100644 (file)
@@ -40,7 +40,7 @@ def _load_crypto_libcrypto():
     #
     # 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,
     #     const unsigned long length, const AES_KEY *key, unsigned char *ivec, const int enc);
 
@@ -385,17 +385,22 @@ def GetIDString():
     if isNewInstall():
         mungedmac = GetMACAddressMunged()
         if len(mungedmac) > 7:
+            print('Using Munged MAC Address for ID: '+mungedmac)
             return mungedmac
     sernum = GetVolumeSerialNumber()
     if len(sernum) > 7:
+        print('Using Volume Serial Number for ID: '+sernum)
         return sernum
     diskpart = GetUserHomeAppSupKindleDirParitionName()
     uuidnum = GetDiskPartitionUUID(diskpart)
     if len(uuidnum) > 7:
+        print('Using Disk Partition UUID for ID: '+uuidnum)
         return uuidnum
     mungedmac = GetMACAddressMunged()
     if len(mungedmac) > 7:
+        print('Using Munged MAC Address for ID: '+mungedmac)
         return mungedmac
+    print('Using Fixed constant 9999999999 for ID.')
     return '9999999999'
 
 
@@ -498,6 +503,7 @@ def getKindleInfoFiles(kInfoFiles):
     for resline in reslst:
         if os.path.isfile(resline):
             kInfoFiles.append(resline)
+            print('Found K4Mac kindle-info file: ' + resline)
             found = True
     # add any .rainier*-kinf files
     cmdline = 'find "' + home + '/Library/Application Support" -name ".rainier*-kinf"'
@@ -508,6 +514,7 @@ def getKindleInfoFiles(kInfoFiles):
     for resline in reslst:
         if os.path.isfile(resline):
             kInfoFiles.append(resline)
+            print('Found k4Mac kinf file: ' + resline)
             found = True
     # add any .kinf2011 files
     cmdline = 'find "' + home + '/Library/Application Support" -name ".kinf2011"'
@@ -518,9 +525,10 @@ def getKindleInfoFiles(kInfoFiles):
     for resline in reslst:
         if os.path.isfile(resline):
             kInfoFiles.append(resline)
+            print('Found k4Mac kinf2011 file: ' + resline)
             found = True
     if not found:
-        print('No kindle-info files have been found.')
+        print('No k4Mac kindle-info/kinf/kinf2011 files have been found.')
     return kInfoFiles
 
 # determine type of kindle info provided and return a
index 88314f67c1bf33711c873f976a56bbd4a8096e43..1bd256234dfd4159fa15113db0efa2993ee80e5d 100644 (file)
@@ -151,7 +151,9 @@ def GetVolumeSerialNumber():
 GetVolumeSerialNumber = GetVolumeSerialNumber()
 
 def GetIDString():
-    return GetVolumeSerialNumber()
+    vsn = GetVolumeSerialNumber()
+    print('Using Volume Serial Number for ID: '+vsn)
+    return vsn
 
 def getLastError():
     GetLastError = kernel32.GetLastError
@@ -210,37 +212,40 @@ def getKindleInfoFiles(kInfoFiles):
     if 'LOCALAPPDATA' in os.environ.keys():
         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
     kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info'
-    if not os.path.isfile(kinfopath):
-        print('No kindle.info files have not been found.')
-    else:
+    if os.path.isfile(kinfopath):
+        found = True
+        print('Found K4PC kindle.info file: ' + kinfopath)
         kInfoFiles.append(kinfopath)
 
     # 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'
-    if not os.path.isfile(kinfopath):
-        print('No K4PC 1.5.X .kinf files have not been found.')
-    else:
+    if os.path.isfile(kinfopath):
+        found = True
+        print('Found K4PC 1.5.X kinf file: ' + kinfopath)
         kInfoFiles.append(kinfopath)
 
     # 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'
-    if not os.path.isfile(kinfopath):
-        print('No K4PC 1.6.X .kinf files have not been found.')
-    else:
+    if os.path.isfile(kinfopath):
+        found = True
+        print('Found K4PC 1.6.X kinf file: ' + kinfopath)
         kInfoFiles.append(kinfopath)
 
     # now look for even newer (K4PC 1.9.0 and later) .kinf2011 file
     kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2011'
-    if not os.path.isfile(kinfopath):
-        print('No K4PC 1.9.X .kinf files have not been found.')
-    else:
+    if os.path.isfile(kinfopath):
+        found = True
+        print('Found K4PC kinf2011 file: ' + kinfopath)
         kInfoFiles.append(kinfopath)
 
+    if not found:
+        print('No K4PC kindle.info/kinf/kinf2011 files have been found.')
     return kInfoFiles
 
 
index c4e45be1daedaaa1a7ca1a0af4da6c2dc1745202..40d84ad713c427faa5b25b18a9a4a2cdcf100a6e 100644 (file)
@@ -262,9 +262,15 @@ def getPidList(md1, md2, k4, pids, serials, kInfoFiles):
     if k4:
         kInfoFiles = getKindleInfoFiles(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:
-        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:
         pidlst.append(pid)
     return pidlst
index b6275d48a0d3ea58037aada8a72194db730b9d02..1ad2bacca76e28c4a5197e245bc203dcec390f5b 100644 (file)
 #  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.35'
+
+__version__ = '0.37'
 
 import sys
 
@@ -168,9 +171,10 @@ class MobiBook:
         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())
+    def __init__(self, infile, announce = True):
+        if announce:
+            print ('MobiDeDrm v%(__version__)s. '
+               'Copyright 2008-2012 The Dark Reverser et al.' % globals())
 
         # initial sanity check on file
         self.data_file = file(infile, 'rb').read()
@@ -198,6 +202,7 @@ class MobiBook:
             print "Book has format: ", self.magic
             self.extra_data_flags = 0
             self.mobi_length = 0
+            self.mobi_codepage = 1252
             self.mobi_version = -1
             self.meta_array = {}
             return
@@ -248,18 +253,19 @@ class MobiBook:
             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]
+        codec = 'windows-1252'
+        if self.magic == 'BOOKMOBI':
+            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 self.mobi_codepage in codec_map.keys():
+                codec = codec_map[self.mobi_codepage]
         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):
@@ -375,7 +381,7 @@ class MobiBook:
                 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. 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
             self.patchSection(0, "\0" * drm_size, drm_ptr)
             # kill the drm pointers
@@ -411,26 +417,26 @@ class MobiBook:
         print "done"
         return
 
-def getUnencryptedBook(infile,pid):
+def getUnencryptedBook(infile,pid,announce=True):
     if not os.path.isfile(infile):
         raise DrmException('Input File Not Found')
-    book = MobiBook(infile)
+    book = MobiBook(infile,announce)
     book.processBook([pid])
     return book.mobi_data
 
-def getUnencryptedBookWithList(infile,pidlist):
+def getUnencryptedBookWithList(infile,pidlist,announce=True):
     if not os.path.isfile(infile):
         raise DrmException('Input File Not Found')
-    book = MobiBook(infile)
+    book = MobiBook(infile, announce)
     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())
+        'Copyright 2008-2012 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 "Removes protection from Kindle/Mobipocket, Kindle/KF8 and Kindle/Print Replica ebooks"
         print "Usage:"
         print "    %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0]
         return 1
@@ -442,7 +448,7 @@ def main(argv=sys.argv):
         else:
             pidlist = {}
         try:
-            stripped_file = getUnencryptedBookWithList(infile, pidlist)
+            stripped_file = getUnencryptedBookWithList(infile, pidlist, False)
             file(outfile, 'wb').write(stripped_file)
         except DrmException, e:
             print "Error: %s" % e
index adbac4988187e1dc4eb4ce403eb931039069ff6b..2347f6aeb128bd5cdfc22f8d52dc5d8c05ba63b2 100644 (file)
@@ -164,6 +164,9 @@ class DocParser(object):
                                 scale = self.pw
                             elif attr == 'line-space':
                                 scale = self.fontsize * 2.0
+                            
+                            if val == "":
+                                val = 0
 
                             if not ((attr == 'hang') and (int(val) == 0)) :
                                 pv = float(val)/scale
index 6afb7daf1c7af8d52f8a0839b9d31fdea3a87978..f1d8574d1bee52c60b39c5215703217336251e2b 100644 (file)
@@ -31,11 +31,8 @@ class TpzDRMError(Exception):
 # local support routines
 if inCalibre:
     from calibre_plugins.k4mobidedrm import kgenpids
-    from calibre_plugins.k4mobidedrm import genbook
 else:
     import kgenpids
-    import genbook
-
 
 # recursive zip creation support routine
 def zipUpDir(myzip, tdir, localname):
@@ -271,6 +268,11 @@ class TopazBook:
             self.createBookDirectory()
             self.extractFiles()
             print "Successfully Extracted Topaz contents"
+            if inCalibre:
+                from calibre_plugins.k4mobidedrm import genbook
+            else:
+                import genbook
+            
             rv = genbook.generateBook(self.outdir, raw, fixedimage)
             if rv == 0:
                 print "\nBook Successfully generated"
@@ -300,6 +302,11 @@ class TopazBook:
         self.createBookDirectory()
         self.extractFiles()
         print "Successfully Extracted Topaz contents"
+        if inCalibre:
+            from calibre_plugins.k4mobidedrm import genbook
+        else:
+            import genbook
+        
         rv = genbook.generateBook(self.outdir, raw, fixedimage)
         if rv == 0:
             print "\nBook Successfully generated"
index 6c9a46506461eadb9bd60ab288aa1e7b4b6ce9ad..1ad2bacca76e28c4a5197e245bc203dcec390f5b 100644 (file)
@@ -27,8 +27,8 @@
 #         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. 
+#         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
@@ -42,7 +42,7 @@
 #  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.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.31 - The multibyte encrytion info is true for version 7 files too.
 #  0.32 - Added support for "Print Replica" Kindle ebooks
 #  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
 
@@ -73,6 +78,7 @@ sys.stdout=Unbuffered(sys.stdout)
 import os
 import struct
 import binascii
+from alfcrypto import Pukall_Cipher
 
 class DrmException(Exception):
     pass
@@ -84,36 +90,37 @@ class DrmException(Exception):
 
 # 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
+    return Pukall_Cipher().PC1(key,src,decryption)
+#     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"
@@ -164,9 +171,10 @@ class MobiBook:
         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())
+    def __init__(self, infile, announce = True):
+        if announce:
+            print ('MobiDeDrm v%(__version__)s. '
+               'Copyright 2008-2012 The Dark Reverser et al.' % globals())
 
         # initial sanity check on file
         self.data_file = file(infile, 'rb').read()
@@ -194,6 +202,7 @@ class MobiBook:
             print "Book has format: ", self.magic
             self.extra_data_flags = 0
             self.mobi_length = 0
+            self.mobi_codepage = 1252
             self.mobi_version = -1
             self.meta_array = {}
             return
@@ -237,25 +246,26 @@ class MobiBook:
             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]
+        codec = 'windows-1252'
+        if self.magic == 'BOOKMOBI':
+            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 self.mobi_codepage in codec_map.keys():
+                codec = codec_map[self.mobi_codepage]
         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):
@@ -320,6 +330,9 @@ class MobiBook:
 
     def getMobiFile(self, outpath):
         file(outpath,'wb').write(self.mobi_data)
+
+    def getMobiVersion(self):
+        return self.mobi_version
         
     def getPrintReplica(self):
         return self.print_replica
@@ -356,9 +369,9 @@ class MobiBook:
             if self.magic == 'TEXtREAd':
                 bookkey_data = self.sect[0x0E:0x0E+16]
             elif self.mobi_version < 0:
-                bookkey_data = self.sect[0x90:0x90+16] 
+                bookkey_data = self.sect[0x90:0x90+16]
             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"
             found_key = PC1(t1_keyvec, bookkey_data)
         else :
@@ -368,12 +381,12 @@ class MobiBook:
                 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.")
+                raise DrmException("No key found in " + str(len(goodpids)) + " keys tried. Please report this failure for help.")
             # 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:
@@ -404,26 +417,26 @@ class MobiBook:
         print "done"
         return
 
-def getUnencryptedBook(infile,pid):
+def getUnencryptedBook(infile,pid,announce=True):
     if not os.path.isfile(infile):
         raise DrmException('Input File Not Found')
-    book = MobiBook(infile)
+    book = MobiBook(infile,announce)
     book.processBook([pid])
     return book.mobi_data
 
-def getUnencryptedBookWithList(infile,pidlist):
+def getUnencryptedBookWithList(infile,pidlist,announce=True):
     if not os.path.isfile(infile):
         raise DrmException('Input File Not Found')
-    book = MobiBook(infile)
+    book = MobiBook(infile, announce)
     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())
+        'Copyright 2008-2012 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 "Removes protection from Kindle/Mobipocket, Kindle/KF8 and Kindle/Print Replica ebooks"
         print "Usage:"
         print "    %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0]
         return 1
@@ -435,7 +448,7 @@ def main(argv=sys.argv):
         else:
             pidlist = {}
         try:
-            stripped_file = getUnencryptedBookWithList(infile, pidlist)
+            stripped_file = getUnencryptedBookWithList(infile, pidlist, False)
             file(outfile, 'wb').write(stripped_file)
         except DrmException, e:
             print "Error: %s" % e
index 1437708372d3020386223b0069267af88bb1737d..20721d170461b84b8a41f25c6f98485b2810bf4e 100644 (file)
@@ -1,5 +1,5 @@
 #! /usr/bin/env python
-# ineptpdf.pyw, version 7.9
+# ineptpdf.pyw, version 7.11
 
 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.8 - Modify interface to allow use of import
 #   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.
@@ -2200,11 +2202,19 @@ class DecryptionDialog(Tkinter.Frame):
 
 def decryptBook(keypath, inpath, outpath):
     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
         with open(outpath, 'wb') as outf:
         # 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
 
 
diff --git a/Other_Tools/KindleBooks/lib/config.py b/Other_Tools/KindleBooks/lib/config.py
new file mode 100644 (file)
index 0000000..c029760
--- /dev/null
@@ -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
index 5312a38b2d2be6f4fdff8ac2cfe68b16c3960520..c412d7b1ba2e38eb8c61ab119fd2b2877cccde2a 100644 (file)
@@ -214,6 +214,7 @@ class PageParser(object):
         'links.title'  : (1, 'text', 0, 0),
         'links.href'   : (1, 'text', 0, 0),
         'links.type'   : (1, 'text', 0, 0),
+        'links.id'     : (1, 'number', 0, 0),
 
         'paraCont'          : (0, 'number', 1, 1),
         'paraCont.rootID'   : (1, 'number', 0, 0),
@@ -239,6 +240,7 @@ class PageParser(object):
         'group'           : (1, 'snippets', 1, 0),
         'group.type'      : (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.type'      : (1, 'scalar_text', 0, 0),
@@ -246,7 +248,7 @@ class PageParser(object):
         'region.y'         : (1, 'scalar_number', 0, 0),
         'region.h'         : (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),
 
diff --git a/Other_Tools/KindleBooks/lib/getk4pcpids.py b/Other_Tools/KindleBooks/lib/getk4pcpids.py
new file mode 100644 (file)
index 0000000..c4716bd
--- /dev/null
@@ -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())
index a889564b8a4b2c8658243bf5eb5e03d2610ea0d2..4056690ade25589e64b2256d3f197248b399401f 100644 (file)
@@ -17,7 +17,7 @@ from __future__ import with_statement
 #    and many many others
 
 
-__version__ = '4.2'
+__version__ = '4.4'
 
 class Unbuffered:
     def __init__(self, stream):
@@ -58,7 +58,7 @@ else:
 # borrowed from calibre from calibre/src/calibre/__init__.py
 # added in removal of non-printing chars
 # 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):
     _filename_sanitize = re.compile(r'[\xae\0\\|\?\*<":>\+/]')
     substitute='_'
@@ -73,7 +73,7 @@ def cleanup_name(name):
     # Mac and Unix don't like file names that begin with a full stop
     if len(one) > 0 and one[0] == '.':
         one = substitute+one[1:]
-    one = one.replace(' ','_')
+    one = one.replace('_',' ')
     return one
 
 def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
@@ -99,11 +99,20 @@ def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
     title = mb.getBookTitle()
     print "Processing Book: ", title
     filenametitle = cleanup_name(title)
-    outfilename = bookname
-    if len(outfilename)<=8 or len(filenametitle)<=8:
-        outfilename = outfilename + "_" + filenametitle
-    elif outfilename[:8] != filenametitle[:8]:
-        outfilename = outfilename[:8] + "_" + filenametitle
+    outfilename = cleanup_name(bookname)
+    
+    # generate 'sensible' filename, that will sort with the original name,
+    # but is close to the name from the file.
+    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
     if len(outfilename)>150:
index e66e9f3c76ec750d283d6dc12542011256a85db9..e51b094ef89dc4c0522b0e7d2d5c4951812d696f 100644 (file)
@@ -385,17 +385,22 @@ def GetIDString():
     if isNewInstall():
         mungedmac = GetMACAddressMunged()
         if len(mungedmac) > 7:
+            print('Using Munged MAC Address for ID: '+mungedmac)
             return mungedmac
     sernum = GetVolumeSerialNumber()
     if len(sernum) > 7:
+        print('Using Volume Serial Number for ID: '+sernum)
         return sernum
     diskpart = GetUserHomeAppSupKindleDirParitionName()
     uuidnum = GetDiskPartitionUUID(diskpart)
     if len(uuidnum) > 7:
+        print('Using Disk Partition UUID for ID: '+uuidnum)
         return uuidnum
     mungedmac = GetMACAddressMunged()
     if len(mungedmac) > 7:
+        print('Using Munged MAC Address for ID: '+mungedmac)
         return mungedmac
+    print('Using Fixed constant 9999999999 for ID.')
     return '9999999999'
 
 
@@ -498,6 +503,7 @@ def getKindleInfoFiles(kInfoFiles):
     for resline in reslst:
         if os.path.isfile(resline):
             kInfoFiles.append(resline)
+            print('Found K4Mac kindle-info file: ' + resline)
             found = True
     # add any .rainier*-kinf files
     cmdline = 'find "' + home + '/Library/Application Support" -name ".rainier*-kinf"'
@@ -508,6 +514,7 @@ def getKindleInfoFiles(kInfoFiles):
     for resline in reslst:
         if os.path.isfile(resline):
             kInfoFiles.append(resline)
+            print('Found k4Mac kinf file: ' + resline)
             found = True
     # add any .kinf2011 files
     cmdline = 'find "' + home + '/Library/Application Support" -name ".kinf2011"'
@@ -518,9 +525,10 @@ def getKindleInfoFiles(kInfoFiles):
     for resline in reslst:
         if os.path.isfile(resline):
             kInfoFiles.append(resline)
+            print('Found k4Mac kinf2011 file: ' + resline)
             found = True
     if not found:
-        print('No kindle-info files have been found.')
+        print('No k4Mac kindle-info/kinf/kinf2011 files have been found.')
     return kInfoFiles
 
 # determine type of kindle info provided and return a
index 88314f67c1bf33711c873f976a56bbd4a8096e43..1bd256234dfd4159fa15113db0efa2993ee80e5d 100644 (file)
@@ -151,7 +151,9 @@ def GetVolumeSerialNumber():
 GetVolumeSerialNumber = GetVolumeSerialNumber()
 
 def GetIDString():
-    return GetVolumeSerialNumber()
+    vsn = GetVolumeSerialNumber()
+    print('Using Volume Serial Number for ID: '+vsn)
+    return vsn
 
 def getLastError():
     GetLastError = kernel32.GetLastError
@@ -210,37 +212,40 @@ def getKindleInfoFiles(kInfoFiles):
     if 'LOCALAPPDATA' in os.environ.keys():
         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
     kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info'
-    if not os.path.isfile(kinfopath):
-        print('No kindle.info files have not been found.')
-    else:
+    if os.path.isfile(kinfopath):
+        found = True
+        print('Found K4PC kindle.info file: ' + kinfopath)
         kInfoFiles.append(kinfopath)
 
     # 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'
-    if not os.path.isfile(kinfopath):
-        print('No K4PC 1.5.X .kinf files have not been found.')
-    else:
+    if os.path.isfile(kinfopath):
+        found = True
+        print('Found K4PC 1.5.X kinf file: ' + kinfopath)
         kInfoFiles.append(kinfopath)
 
     # 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'
-    if not os.path.isfile(kinfopath):
-        print('No K4PC 1.6.X .kinf files have not been found.')
-    else:
+    if os.path.isfile(kinfopath):
+        found = True
+        print('Found K4PC 1.6.X kinf file: ' + kinfopath)
         kInfoFiles.append(kinfopath)
 
     # now look for even newer (K4PC 1.9.0 and later) .kinf2011 file
     kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2011'
-    if not os.path.isfile(kinfopath):
-        print('No K4PC 1.9.X .kinf files have not been found.')
-    else:
+    if os.path.isfile(kinfopath):
+        found = True
+        print('Found K4PC kinf2011 file: ' + kinfopath)
         kInfoFiles.append(kinfopath)
 
+    if not found:
+        print('No K4PC kindle.info/kinf/kinf2011 files have been found.')
     return kInfoFiles
 
 
index abfc7e47f6140c907af2fc967d414ef703d3fce4..40d84ad713c427faa5b25b18a9a4a2cdcf100a6e 100644 (file)
@@ -62,7 +62,7 @@ def encode(data, 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)
@@ -78,11 +78,11 @@ def decode(data,map):
         value = (((high * len(map)) ^ 0x80) & 0xFF) + low
         result += pack("B",value)
     return result
-    
+
 #
 # PID generation routines
 #
-  
+
 # Returns two bit at offset from a bit field
 def getTwoBitsFromBitField(bitField,offset):
     byteNumber = offset // 4
@@ -91,10 +91,10 @@ def getTwoBitsFromBitField(bitField,offset):
 
 # 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
-     
+    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
@@ -121,8 +121,8 @@ def generatePidEncryptionTable() :
 def generatePidSeed(table,dsn) :
     value = 0
     for counter in range (0,4) :
-       index = (ord(dsn[counter]) ^ value) &0xFF
-       value = (value >> 8) ^ table[index]
+        index = (ord(dsn[counter]) ^ value) &0xFF
+        value = (value >> 8) ^ table[index]
     return value
 
 # Generate the device PID
@@ -141,7 +141,7 @@ def generateDevicePID(table,dsn,nbRoll):
     return pidAscii
 
 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
 def checksumPid(s):
@@ -204,10 +204,10 @@ def getK4Pids(pidlst, rec209, token, kInfoFile):
         print(message)
         kindleDatabase = None
         pass
-    
+
     if kindleDatabase == None :
         return pidlst
-        
+
     try:
         # Get the Mazama Random number
         MazamaRandomNumber = kindleDatabase["MazamaRandomNumber"]
@@ -217,7 +217,7 @@ def getK4Pids(pidlst, rec209, token, kInfoFile):
     except KeyError:
         print "Keys not found in " + kInfoFile
         return pidlst
-        
+
     # Get the ID string used
     encodedIDString = encodeHash(GetIDString(),charMap1)
 
@@ -226,7 +226,7 @@ def getK4Pids(pidlst, rec209, token, kInfoFile):
 
     # concat, hash and encode to calculate the DSN
     DSN = encode(SHA1(MazamaRandomNumber+encodedIDString+encodedUsername),charMap1)
-       
+
     # Compute the device PID (for which I can tell, is used for nothing).
     table =  generatePidEncryptionTable()
     devicePID = generateDevicePID(table,DSN,4)
@@ -258,13 +258,19 @@ def getK4Pids(pidlst, rec209, token, kInfoFile):
 def getPidList(md1, md2, k4, pids, serials, kInfoFiles):
     pidlst = []
     if kInfoFiles is None:
-       kInfoFiles = []
+        kInfoFiles = []
     if k4:
         kInfoFiles = getKindleInfoFiles(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:
-        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:
         pidlst.append(pid)
     return pidlst
index b6275d48a0d3ea58037aada8a72194db730b9d02..1ad2bacca76e28c4a5197e245bc203dcec390f5b 100644 (file)
 #  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.35'
+
+__version__ = '0.37'
 
 import sys
 
@@ -168,9 +171,10 @@ class MobiBook:
         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())
+    def __init__(self, infile, announce = True):
+        if announce:
+            print ('MobiDeDrm v%(__version__)s. '
+               'Copyright 2008-2012 The Dark Reverser et al.' % globals())
 
         # initial sanity check on file
         self.data_file = file(infile, 'rb').read()
@@ -198,6 +202,7 @@ class MobiBook:
             print "Book has format: ", self.magic
             self.extra_data_flags = 0
             self.mobi_length = 0
+            self.mobi_codepage = 1252
             self.mobi_version = -1
             self.meta_array = {}
             return
@@ -248,18 +253,19 @@ class MobiBook:
             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]
+        codec = 'windows-1252'
+        if self.magic == 'BOOKMOBI':
+            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 self.mobi_codepage in codec_map.keys():
+                codec = codec_map[self.mobi_codepage]
         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):
@@ -375,7 +381,7 @@ class MobiBook:
                 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. 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
             self.patchSection(0, "\0" * drm_size, drm_ptr)
             # kill the drm pointers
@@ -411,26 +417,26 @@ class MobiBook:
         print "done"
         return
 
-def getUnencryptedBook(infile,pid):
+def getUnencryptedBook(infile,pid,announce=True):
     if not os.path.isfile(infile):
         raise DrmException('Input File Not Found')
-    book = MobiBook(infile)
+    book = MobiBook(infile,announce)
     book.processBook([pid])
     return book.mobi_data
 
-def getUnencryptedBookWithList(infile,pidlist):
+def getUnencryptedBookWithList(infile,pidlist,announce=True):
     if not os.path.isfile(infile):
         raise DrmException('Input File Not Found')
-    book = MobiBook(infile)
+    book = MobiBook(infile, announce)
     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())
+        'Copyright 2008-2012 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 "Removes protection from Kindle/Mobipocket, Kindle/KF8 and Kindle/Print Replica ebooks"
         print "Usage:"
         print "    %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0]
         return 1
@@ -442,7 +448,7 @@ def main(argv=sys.argv):
         else:
             pidlist = {}
         try:
-            stripped_file = getUnencryptedBookWithList(infile, pidlist)
+            stripped_file = getUnencryptedBookWithList(infile, pidlist, False)
             file(outfile, 'wb').write(stripped_file)
         except DrmException, e:
             print "Error: %s" % e
index adbac4988187e1dc4eb4ce403eb931039069ff6b..2347f6aeb128bd5cdfc22f8d52dc5d8c05ba63b2 100644 (file)
@@ -164,6 +164,9 @@ class DocParser(object):
                                 scale = self.pw
                             elif attr == 'line-space':
                                 scale = self.fontsize * 2.0
+                            
+                            if val == "":
+                                val = 0
 
                             if not ((attr == 'hang') and (int(val) == 0)) :
                                 pv = float(val)/scale
index 6afb7daf1c7af8d52f8a0839b9d31fdea3a87978..f1d8574d1bee52c60b39c5215703217336251e2b 100644 (file)
@@ -31,11 +31,8 @@ class TpzDRMError(Exception):
 # local support routines
 if inCalibre:
     from calibre_plugins.k4mobidedrm import kgenpids
-    from calibre_plugins.k4mobidedrm import genbook
 else:
     import kgenpids
-    import genbook
-
 
 # recursive zip creation support routine
 def zipUpDir(myzip, tdir, localname):
@@ -271,6 +268,11 @@ class TopazBook:
             self.createBookDirectory()
             self.extractFiles()
             print "Successfully Extracted Topaz contents"
+            if inCalibre:
+                from calibre_plugins.k4mobidedrm import genbook
+            else:
+                import genbook
+            
             rv = genbook.generateBook(self.outdir, raw, fixedimage)
             if rv == 0:
                 print "\nBook Successfully generated"
@@ -300,6 +302,11 @@ class TopazBook:
         self.createBookDirectory()
         self.extractFiles()
         print "Successfully Extracted Topaz contents"
+        if inCalibre:
+            from calibre_plugins.k4mobidedrm import genbook
+        else:
+            import genbook
+        
         rv = genbook.generateBook(self.outdir, raw, fixedimage)
         if rv == 0:
             print "\nBook Successfully generated"
index 3f03182908a9526d100bcac9f226bd05b6c51ef7..3245eb37163cabf91cb9eb915c913257b7374703 100644 (file)
@@ -1,7 +1,7 @@
 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, you’ll 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.
@@ -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.
 
+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 won’t), and you can then download ebooks from the store or library encrypted to work with your Kindle. Except they don’t. There’s a flag byte set in encrypted Kindle ebooks, and Kindles and the Kindle app won’t 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 Kindle’s 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, they’ll automatically become unreadable.
 
+(2012 note: Few, if any, libraries now use this method of loaning ebooks.)
+
 
index c6665c6dc72e3083a85432d554ed5af1fbe75f4a..5a9e4332ea5c85a5f144a32b4b10a3b491a7d2ef 100644 (file)
@@ -1,18 +1,21 @@
 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:
- - eReader PDB books
+
+ - Kindle ebooks (including Mobi, Topaz, Print Replica and KF8).
  - Barnes and Noble ePubs
- - Adobe Digitial Editions ePubs
- - Adobe Digitial Editions PDFs
- - Kindle/Mobipocket ebooks (including Topaz, Print Replica and KF8).
+ - Adobe Digital Editions ePubs
+ - Adobe Digital Editions PDFs
+ - Mobipocket ebooks
+ - eReader PDB books
 
 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 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.
 
@@ -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)
 --------------------------------------------
-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***.
-See below for details on which versions are recommended.
+***This program requires that Python and PyCrypto be properly installed.***
+***See below for details on recommended versions are where to get them.***
+
+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.
 
-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. 
+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.
 
-To use it simply drag ebooks or folders containing ebooks onto the DeDRM_Drop_Target short-cut, and it will process the ebooks.
+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
 ----------------
-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:
 
 
+
+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/)
 --------------------------------------------------
 
@@ -227,7 +255,7 @@ this will launch a window with 3 lines
 2. input file: drmbook.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:
 
 11. install the plugins from the tools as discribed in the readmes for win