diff --git a/Calibre_Plugins/ineptepub_plugin.zip b/Calibre_Plugins/ineptepub_plugin.zip index 31ffcde..562923b 100644 Binary files a/Calibre_Plugins/ineptepub_plugin.zip and b/Calibre_Plugins/ineptepub_plugin.zip differ diff --git a/Calibre_Plugins/ineptepub_plugin/ade_key.py b/Calibre_Plugins/ineptepub_plugin/ade_key.py index 9c7f6cf..eb9ae3d 100644 --- a/Calibre_Plugins/ineptepub_plugin/ade_key.py +++ b/Calibre_Plugins/ineptepub_plugin/ade_key.py @@ -310,35 +310,31 @@ if iswindows: else: import xml.etree.ElementTree as etree - import Carbon.File - import Carbon.Folder - import Carbon.Folders - import MacOS - - ACTIVATION_PATH = 'Adobe/Digital Editions/activation.dat' + import subprocess + NSMAP = {'adept': 'http://ns.adobe.com/adept', 'enc': 'http://www.w3.org/2001/04/xmlenc#'} - - def find_folder(domain, dtype): - try: - fsref = Carbon.Folder.FSFindFolder(domain, dtype, False) - return Carbon.File.pathname(fsref) - except MacOS.Error: - return None - - def find_app_support_file(subpath): - dtype = Carbon.Folders.kApplicationSupportFolderType - for domain in Carbon.Folders.kUserDomain, Carbon.Folders.kLocalDomain: - path = find_folder(domain, dtype) - if path is None: - continue - path = os.path.join(path, subpath) - if os.path.isfile(path): - return path + + def findActivationDat(): + home = os.getenv('HOME') + cmdline = 'find "' + home + '/Library/Application Support/Adobe/Digital Editions" -name "activation.dat"' + cmdline = cmdline.encode(sys.getfilesystemencoding()) + p2 = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False) + out1, out2 = p2.communicate() + reslst = out1.split('\n') + cnt = len(reslst) + for j in xrange(cnt): + resline = reslst[j] + pp = resline.find('activation.dat') + if pp >= 0: + ActDatPath = resline + break + if os.path.exists(ActDatPath): + return ActDatPath return None def retrieve_key(): - actpath = find_app_support_file(ACTIVATION_PATH) + actpath = findActivationDat() if actpath is None: raise ADEPTError("Could not locate ADE activation") tree = etree.parse(actpath) diff --git a/Calibre_Plugins/ineptepub_plugin/ineptepub_plugin.py b/Calibre_Plugins/ineptepub_plugin/ineptepub_plugin.py index ea52f0a..137cc2a 100644 --- a/Calibre_Plugins/ineptepub_plugin/ineptepub_plugin.py +++ b/Calibre_Plugins/ineptepub_plugin/ineptepub_plugin.py @@ -42,7 +42,9 @@ # Revision history: # 0.1 - Initial release # 0.1.1 - Allow Windows users to make use of openssl if they have it installed. -# - Incorporated SomeUpdates zipfix routine. +# - Incorporated SomeUpdates zipfix routine. +# 0.1.2 - Removed Carbon dependency for Mac users. Fixes an issue that was a +# result of Calibre changing to python 2.7. """ @@ -363,7 +365,7 @@ class IneptDeDRM(FileTypePlugin): Credit given to I <3 Cabbages for the original stand-alone scripts.' supported_platforms = ['linux', 'osx', 'windows'] author = 'DiapDealer' - version = (0, 1, 1) + version = (0, 1, 2) minimum_calibre_version = (0, 6, 44) # Compiled python libraries cannot be imported in earlier versions. file_types = set(['epub']) on_import = True @@ -420,7 +422,7 @@ class IneptDeDRM(FileTypePlugin): try: keydata = retrieve_key() userkeys.append(keydata) - keypath = os.path.join(confpath, 'adeptkey.der') + keypath = os.path.join(confpath, 'calibre-adeptkey.der') with open(keypath, 'wb') as f: f.write(keydata) print 'IneptEpub: Created keyfile from ADE install.' diff --git a/Calibre_Plugins/ineptepub_plugin/osx/Carbon/File.pyo b/Calibre_Plugins/ineptepub_plugin/osx/Carbon/File.pyo deleted file mode 100644 index be3ab04..0000000 Binary files a/Calibre_Plugins/ineptepub_plugin/osx/Carbon/File.pyo and /dev/null differ diff --git a/Calibre_Plugins/ineptepub_plugin/osx/Carbon/Folder.pyo b/Calibre_Plugins/ineptepub_plugin/osx/Carbon/Folder.pyo deleted file mode 100644 index 5a82134..0000000 Binary files a/Calibre_Plugins/ineptepub_plugin/osx/Carbon/Folder.pyo and /dev/null differ diff --git a/Calibre_Plugins/ineptepub_plugin/osx/Carbon/Folders.pyo b/Calibre_Plugins/ineptepub_plugin/osx/Carbon/Folders.pyo deleted file mode 100644 index c5a2ee0..0000000 Binary files a/Calibre_Plugins/ineptepub_plugin/osx/Carbon/Folders.pyo and /dev/null differ diff --git a/Calibre_Plugins/ineptepub_plugin/osx/Carbon/__init__.pyo b/Calibre_Plugins/ineptepub_plugin/osx/Carbon/__init__.pyo deleted file mode 100644 index 0f5325d..0000000 Binary files a/Calibre_Plugins/ineptepub_plugin/osx/Carbon/__init__.pyo and /dev/null differ diff --git a/Calibre_Plugins/k4mobidedrm_plugin.zip b/Calibre_Plugins/k4mobidedrm_plugin.zip index 21a61cb..fe8263a 100644 Binary files a/Calibre_Plugins/k4mobidedrm_plugin.zip and b/Calibre_Plugins/k4mobidedrm_plugin.zip differ diff --git a/Calibre_Plugins/k4mobidedrm_plugin/k4mobidedrm_plugin.py b/Calibre_Plugins/k4mobidedrm_plugin/k4mobidedrm_plugin.py index 1ad83bc..2b91046 100644 --- a/Calibre_Plugins/k4mobidedrm_plugin/k4mobidedrm_plugin.py +++ b/Calibre_Plugins/k4mobidedrm_plugin/k4mobidedrm_plugin.py @@ -475,7 +475,7 @@ if not __name__ == "__main__" and inCalibre: Provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc.' supported_platforms = ['osx', 'windows', 'linux'] # Platforms this plugin will run on author = 'DiapDealer, SomeUpdates' # The author of this plugin - version = (0, 1, 3) # The version number of this plugin + version = (0, 1, 4) # The version number of this plugin file_types = set(['prc','mobi','azw']) # The file types that this plugin will be applied to on_import = True # Run this plugin during the import priority = 200 # run this plugin before mobidedrm, k4pcdedrm, k4dedrm @@ -483,6 +483,24 @@ if not __name__ == "__main__" and inCalibre: def run(self, path_to_ebook): from calibre.gui2 import is_ok_to_use_qt from PyQt4.Qt import QMessageBox + + # Head Topaz files off at the pass and warn the user that they will NOT + # be decrypted. Changes the file extension from .azw or .prc to .tpz so + # Calibre can at least read the metadata properly and the user can find + # them by sorting on 'format'. + with open(path_to_ebook, 'rb') as f: + raw = f.read() + if raw.startswith('TPZ'): + tf = self.temporary_file('.tpz') + if is_ok_to_use_qt(): + d = QMessageBox(QMessageBox.Warning, "K4MobiDeDRM Plugin", "%s is a Topaz book. It will NOT be decrypted!" % path_to_ebook) + d.show() + d.raise_() + d.exec_() + tf.write(raw) + tf.close + return tf.name + global kindleDatabase global openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap1, charMap2, charMap3, charMap4 if sys.platform.startswith('win'): diff --git a/Calibre_Plugins/k4mobidedrm_plugin/mobidedrm.py b/Calibre_Plugins/k4mobidedrm_plugin/mobidedrm.py index 9f17a3b..183432c 100644 --- a/Calibre_Plugins/k4mobidedrm_plugin/mobidedrm.py +++ b/Calibre_Plugins/k4mobidedrm_plugin/mobidedrm.py @@ -39,8 +39,9 @@ # 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 - Corretion: It seems that multibyte entries are encrypted in a v6 file. -__version__ = '0.19' +__version__ = '0.20' import sys import struct @@ -208,8 +209,8 @@ class DrmStripper: if (mobi_length >= 0xE4) and (mobi_version >= 5): extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4]) print "Extra Data Flags = %d" %extra_data_flags - if mobi_version <= 5: - # multibyte utf8 data is included in the encryption for mobi_version 5 and below + if mobi_version < 7: + # multibyte utf8 data is included in the encryption for mobi_version 6 and below # so clear that byte so that we leave it to be decrypted. extra_data_flags &= 0xFFFE diff --git a/DeDRM_Macintosh_Application/DeDRM.app.txt b/DeDRM_Macintosh_Application/DeDRM.app.txt index 099e256..63a9790 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app.txt +++ b/DeDRM_Macintosh_Application/DeDRM.app.txt @@ -162,7 +162,7 @@ on unlockpdbfile(encryptedFile) set sourcePath to (POSIX path of file parent_folder) & fileName & "_Source/" set pmlzFilePath to POSIX path of file parent_folder & fileName & "_dedrmed.pmlz" if length of bnKeys is 0 then - GetKeys() + GetKeys(false) end if set shellresult to "Error: No eReader/Barnes & Noble Name:Number keys supplied. Can't decrypt." repeat with BNKey in bnKeys @@ -367,7 +367,7 @@ end unlockepubfile on unlockpdffile(encryptedFile) --check it's an ePub file. - set PDFSig to "NOT_DF" + set PDFSig to "NOT_PDF" try set PDFSig to read file encryptedFile from 1 for 4 end try @@ -393,23 +393,7 @@ on unlockpdffile(encryptedFile) set decoded to "NO" -- first we must check we have a PDF script - if not fileexists(AdobePDFTool) then - set newFile to AdobePDFTool - try - tell me to activate - set newFile to ((choose file with prompt "Please find the ineptpdf script") as text) - end try - if not fileexists(newFile) then - set ErrorCount to ErrorCount + 1 - set ErrorList to ErrorList & encryptedFile & " is a PDF file and no ineptpdf script found. - -" - return - end if - -- set AdobePDFTool to POSIX path of file CopyToPrefs(newFile) - set AdobePDFTool to POSIX path of file newFile - WritePrefs() - end if + GetIneptPDF(false) if not fileexists(AdobePDFTool) then set ErrorCount to ErrorCount + 1 set ErrorList to ErrorList & encryptedFile & " is a PDF file and no ineptpdf script found. @@ -596,7 +580,7 @@ on GetPIDs() Enter any additional Mobipocket PIDs for your Mobipocket books one at a time:" set FinishedButton to "No More" end if - set dialogresult to (display dialog DialogPrompt default answer "" buttons {"Delete All", "Add", FinishedButton} with title "DeDRM Applescript" default button 2) + set dialogresult to (display dialog DialogPrompt default answer "" buttons {"Delete All", "Add", FinishedButton} with title "DeDRM Applescript 2/5" default button 2) if button returned of dialogresult is "Add" then set PID to text returned of dialogresult set PIDlength to length of PID @@ -657,7 +641,37 @@ To add extra Kindle Info files, click the Add end repeat end GetKindleInfoFiles -on GetKeys() +on GetIneptPDF(always) + if always or not fileexists(AdobePDFTool) then + set newFile to "" + try + tell me to activate + if (always) then + set promptstring to "DeDRM Applescript 5/5 +" + else + set promptstring to "DeDRM Applescript +" + end if + if fileexists(AdobePDFTool) then + set promptstring to promptstring & "Please find the new ineptpdf script or click Cancel if the path shown is correct: +" & ((POSIX file AdobePDFTool) as text) + else + set promptstring to promptstring & "Please find the ineptpdf script to be able to dedrm PDF files." + end if + set newFile to ((choose file with prompt promptstring default location POSIX file AdobePDFTool) as text) + --on error errormessage + -- display dialog errormessage + end try + if fileexists(newFile) then + -- set AdobePDFTool to POSIX path of file CopyToPrefs(newFile) + set AdobePDFTool to POSIX path of file newFile + WritePrefs() + end if + end if +end GetIneptPDF + +on GetKeys(running) set bnKeyText to "" repeat set BNKeystring to GetBNKeystring() @@ -671,9 +685,12 @@ on GetKeys() Please enter any additional " set FinishedButton to "No More" end if - set DialogPrompt to DialogPrompt & "eReader/Barnes & Noble Name:Number key pairs one at a time. If you're only decoding eReader files, the last 8 digits of the Number will do. The full 16 are only needed for Barnes & Noble ePubs. Only the last eight will be stored or displayed. Please separate the name and number with a colon and click \"Add\". Or to add a an already generated .b64 file, just click \"Add\" with nothing in the text field." - - set dialogresult to (display dialog DialogPrompt default answer bnKeyText buttons {"Delete All", "Add", FinishedButton} with title "DeDRM Applescript" default button 2) + set DialogPrompt to DialogPrompt & "eReader/Barnes & Noble Name,Number key pairs one at a time. If you're only decoding eReader files, the last 8 digits of the Number will do. The full 15 or 16 are only needed for Barnes & Noble ePubs. Only the last eight will be stored or displayed. Please separate the name and number with a comma and click \"Add\". Or to add a an already generated .b64 file, just click \"Add\" with nothing in the text field." + set dialogtitle to "DeDRM Applescript" + if (running) then + set dialogtitle to dialogtitle & " 3/5" + end if + set dialogresult to (display dialog DialogPrompt default answer bnKeyText buttons {"Delete All", "Add", FinishedButton} with title dialogtitle default button 2) if button returned of dialogresult is "Add" then set bnKeyText to text returned of dialogresult if bnKeyText is "" then @@ -686,17 +703,17 @@ Please enter any additional " display dialog message end if end try - else if not (bnKeyText contains ":") then - display dialog "Name and Number must be separated by a colon (:)." buttons {"OK"} default button 1 with title "DeDRM Applescript" with icon caution + else if not (bnKeyText contains ",") then + display dialog "Name and Number must be separated by a comma (,)." buttons {"OK"} default button 1 with title "DeDRM Applescript" with icon caution else - set AppleScript's text item delimiters to ":" + set AppleScript's text item delimiters to "," set keyNumber to the last text item of bnKeyText set keyName to (text items 1 through -2 of bnKeyText) as string - if ((length of keyNumber) = 16 or (length of keyNumber) = 8) and (length of keyName) > 0 then + if ((length of keyNumber) = 16 or (length of keyNumber) = 15 or (length of keyNumber) = 8) and (length of keyName) > 0 then set shellresult to "" set keyfilepath to "" - if (length of keyNumber) = 16 then + if (length of keyNumber) = 16 or (length of keyNumber) = 15 then -- get the B&N key from this pair set shellresult to "no result" set scriptError to "Key Gen Script failed." @@ -717,7 +734,7 @@ Please enter any additional " display dialog "Error generating key from this info, error message was: " & scriptError buttons {"OK"} default button 1 with title "DeDRM Applescript" with icon caution end if else - display dialog "Key numbers must be 8 or 16 characters long (no spaces) and the key name must not be absent." buttons {"OK"} default button 1 with title "DeDRM Applescript" with icon caution + display dialog "Key numbers must be 8 or 15 or 16 characters long (no spaces) and the key name must not be absent." buttons {"OK"} default button 1 with title "DeDRM Applescript" with icon caution end if end if else if button returned of dialogresult is "Delete All" then @@ -759,7 +776,7 @@ on GetAdeptKeyFiles() To add extra key files (.der), click the AddÉ button." set FinishedButton to "No More" end if - set dialogresult to (display dialog DialogPrompt buttons {"Forget All", "AddÉ", FinishedButton} with title "DeDRM Applescript" default button 2) + set dialogresult to (display dialog DialogPrompt buttons {"Forget All", "AddÉ", FinishedButton} with title "DeDRM Applescript 4/5" default button 2) if button returned of dialogresult is "AddÉ" then try set newFile to (choose file with prompt "Please select an Adept key file") as text @@ -1044,12 +1061,13 @@ Anyone is free to copy, modify, publish, use, compile, sell, or distribute this For more information, please refer to -" with title "DeDRM Applescript" buttons {"Cancel", "Continue"} default button 2 +" with title "DeDRM Applescript 1/5" buttons {"Cancel", "Continue"} default button 2 ReadPrefs() GetPIDs() - GetKeys() + GetKeys(true) GetAdeptKey(true) GetAdeptKeyFiles() + GetIneptPDF(true) --GetKindleInfoFiles() WritePrefs() end if diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Info.plist b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Info.plist index 330ec11..31358b6 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Info.plist +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Info.plist @@ -24,7 +24,7 @@ CFBundleExecutable droplet CFBundleGetInfoString - DeDRM 1.2, Copyright © 2010 by Apprentice Alf. + DeDRM 1.3, Copyright © 2010 by Apprentice Alf. CFBundleIconFile droplet CFBundleInfoDictionaryVersion @@ -34,7 +34,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.2 + 1.3 CFBundleSignature dplt LSMinimumSystemVersion @@ -46,9 +46,9 @@ name ScriptWindowState positionOfDivider - 885 + 739 savedFrame - 1507 -64 1262 964 1440 -150 1680 1050 + 1533 -24 1262 818 1440 -150 1680 1050 selectedTabView result diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/Scripts/main.scpt b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/Scripts/main.scpt index 2a37d51..86a1173 100644 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/mobidedrm.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/mobidedrm.py index 9f17a3b..183432c 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/mobidedrm.py +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/mobidedrm.py @@ -39,8 +39,9 @@ # 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 - Corretion: It seems that multibyte entries are encrypted in a v6 file. -__version__ = '0.19' +__version__ = '0.20' import sys import struct @@ -208,8 +209,8 @@ class DrmStripper: if (mobi_length >= 0xE4) and (mobi_version >= 5): extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4]) print "Extra Data Flags = %d" %extra_data_flags - if mobi_version <= 5: - # multibyte utf8 data is included in the encryption for mobi_version 5 and below + if mobi_version < 7: + # multibyte utf8 data is included in the encryption for mobi_version 6 and below # so clear that byte so that we leave it to be decrypted. extra_data_flags &= 0xFFFE diff --git a/Kindle_Mobi_Tools/FindTopazEbooks.pyw b/Kindle_Mobi_Tools/FindTopazEbooks.pyw new file mode 100644 index 0000000..6a0df30 --- /dev/null +++ b/Kindle_Mobi_Tools/FindTopazEbooks.pyw @@ -0,0 +1,216 @@ +#!/usr/bin/env python + +# This is a simple tool to identify all Amazon Topaz ebooks in a specific directory. +# There always seems to be confusion since Topaz books downloaded to K4PC/Mac can have +# almost any extension (.azw, .azw1, .prc, tpz). While the .azw1 and .tpz extensions +# are fairly easy to indentify, the others are not (without opening the files in an editor). + +# To run the tool with the GUI frontend, just double-click on the 'FindTopazFiles.pyw' file +# and select the folder where all of the ebooks in question are located. Then click 'Search'. +# The program will list the file names of the ebooks that are indentified as being Topaz. +# You can then isolate those books and use the Topaz tools to decrypt and convert them. + +# You can also run the script from a command line... supplying the folder to search +# as a parameter: python FindTopazEbooks.pyw "C:\My Folder" (change appropriately for +# your particular O.S.) + +# ** NOTE: This program does NOT decrypt or modify Topaz files in any way. It simply identifies them. + +# PLEASE DO NOT PIRATE EBOOKS! + +# We want all authors and publishers, and eBook stores to live +# long and prosperous lives but at the same time we just want to +# be able to read OUR books on whatever device we want and to keep +# readable for a long, long time + +# This borrows very heavily from works by CMBDTC, IHeartCabbages, skindle, +# unswindle, DarkReverser, ApprenticeAlf, DiapDealer, some_updates +# and many many others + +# Revision history: +# 1 - Initial release. + +from __future__ import with_statement + +__license__ = 'GPL v3' + +import sys +import os +import re +import shutil +import Tkinter +import Tkconstants +import tkFileDialog +import tkMessageBox + + +class ScrolledText(Tkinter.Text): + def __init__(self, master=None, **kw): + self.frame = Tkinter.Frame(master) + self.vbar = Tkinter.Scrollbar(self.frame) + self.vbar.pack(side=Tkconstants.RIGHT, fill=Tkconstants.Y) + kw.update({'yscrollcommand': self.vbar.set}) + Tkinter.Text.__init__(self, self.frame, **kw) + self.pack(side=Tkconstants.LEFT, fill=Tkconstants.BOTH, expand=True) + self.vbar['command'] = self.yview + # Copy geometry methods of self.frame without overriding Text + # methods = hack! + text_meths = vars(Tkinter.Text).keys() + methods = vars(Tkinter.Pack).keys() + vars(Tkinter.Grid).keys() + vars(Tkinter.Place).keys() + methods = set(methods).difference(text_meths) + for m in methods: + if m[0] != '_' and m != 'config' and m != 'configure': + setattr(self, m, getattr(self.frame, m)) + + def __str__(self): + return str(self.frame) + + +def cli_main(argv=sys.argv, obj=None): + progname = os.path.basename(argv[0]) + if len(argv) != 2: + print "usage: %s DIRECTORY" % (progname,) + return 1 + + if obj == None: + print "\nTopaz search results:\n" + else: + obj.stext.insert(Tkconstants.END,"Topaz search results:\n\n") + + inpath = argv[1] + files = os.listdir(inpath) + filefilter = re.compile("(\.azw$)|(\.azw1$)|(\.prc$)|(\.tpz$)", re.IGNORECASE) + files = filter(filefilter.search, files) + + if files: + topazcount = 0 + totalcount = 0 + for filename in files: + with open(os.path.join(inpath, filename), 'rb') as f: + try: + if f.read().startswith('TPZ'): + f.close() + basename, extension = os.path.splitext(filename) + if obj == None: + print " %s is a Topaz formatted ebook." % filename + """ + if extension == '.azw' or extension == '.prc': + print " renaming to %s" % (basename + '.tpz') + shutil.move(os.path.join(inpath, filename), + os.path.join(inpath, basename + '.tpz')) + """ + else: + msg1 = " %s is a Topaz formatted ebook.\n" % filename + obj.stext.insert(Tkconstants.END,msg1) + """ + if extension == '.azw' or extension == '.prc': + msg2 = " renaming to %s\n" % (basename + '.tpz') + obj.stext.insert(Tkconstants.END,msg2) + shutil.move(os.path.join(inpath, filename), + os.path.join(inpath, basename + '.tpz')) + """ + topazcount += 1 + except: + if obj == None: + print " Error reading %s." % filename + else: + msg = " Error reading or %s.\n" % filename + obj.stext.insert(Tkconstants.END,msg) + pass + totalcount += 1 + if topazcount == 0: + if obj == None: + print "\nNo Topaz books found in %s." % inpath + else: + msg = "\nNo Topaz books found in %s.\n\n" % inpath + obj.stext.insert(Tkconstants.END,msg) + else: + if obj == None: + print "\n%i Topaz books found in %s\n%i total books checked.\n" % (topazcount, inpath, totalcount) + else: + msg = "\n%i Topaz books found in %s\n%i total books checked.\n\n" %(topazcount, inpath, totalcount) + obj.stext.insert(Tkconstants.END,msg) + else: + if obj == None: + print "No typical Topaz file extensions found in %s.\n" % inpath + else: + msg = "No typical Topaz file extensions found in %s.\n\n" % inpath + obj.stext.insert(Tkconstants.END,msg) + + return 0 + + +class DecryptionDialog(Tkinter.Frame): + def __init__(self, root): + Tkinter.Frame.__init__(self, root, border=5) + ltext='Search a directory for Topaz eBooks\n' + self.status = Tkinter.Label(self, text=ltext) + self.status.pack(fill=Tkconstants.X, expand=1) + body = Tkinter.Frame(self) + body.pack(fill=Tkconstants.X, expand=1) + sticky = Tkconstants.E + Tkconstants.W + body.grid_columnconfigure(1, weight=2) + Tkinter.Label(body, text='Directory to Search').grid(row=1) + self.inpath = Tkinter.Entry(body, width=30) + self.inpath.grid(row=1, column=1, sticky=sticky) + button = Tkinter.Button(body, text="...", command=self.get_inpath) + button.grid(row=1, column=2) + msg1 = 'Topaz search results \n\n' + self.stext = ScrolledText(body, bd=5, relief=Tkconstants.RIDGE, + height=15, width=60, wrap=Tkconstants.WORD) + self.stext.grid(row=4, column=0, columnspan=2,sticky=sticky) + #self.stext.insert(Tkconstants.END,msg1) + buttons = Tkinter.Frame(self) + buttons.pack() + + + self.botton = Tkinter.Button( + buttons, text="Search", width=10, command=self.search) + self.botton.pack(side=Tkconstants.LEFT) + Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT) + self.button = Tkinter.Button( + buttons, text="Quit", width=10, command=self.quit) + self.button.pack(side=Tkconstants.RIGHT) + + def get_inpath(self): + cwd = os.getcwdu() + cwd = cwd.encode('utf-8') + inpath = tkFileDialog.askdirectory( + parent=None, title='Directory to search', + initialdir=cwd, initialfile=None) + if inpath: + inpath = os.path.normpath(inpath) + self.inpath.delete(0, Tkconstants.END) + self.inpath.insert(0, inpath) + return + + + def search(self): + inpath = self.inpath.get() + if not inpath or not os.path.exists(inpath): + self.status['text'] = 'Specified directory does not exist' + return + argv = [sys.argv[0], inpath] + self.status['text'] = 'Searching...' + self.botton.configure(state='disabled') + cli_main(argv, self) + self.status['text'] = 'Search a directory for Topaz files' + self.botton.configure(state='normal') + + return + + +def gui_main(): + root = Tkinter.Tk() + root.title('Topaz eBook Finder') + root.resizable(True, False) + root.minsize(370, 0) + DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1) + root.mainloop() + return 0 + + +if __name__ == '__main__': + if len(sys.argv) > 1: + sys.exit(cli_main()) + sys.exit(gui_main()) \ No newline at end of file diff --git a/Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/K4MobiDeDRM.pyw b/Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/K4MobiDeDRM.pyw index d25c029..c909eb3 100644 --- a/Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/K4MobiDeDRM.pyw +++ b/Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/K4MobiDeDRM.pyw @@ -192,6 +192,19 @@ class MainDialog(Tkinter.Frame): self.status['text'] = 'Specified K4PC, K4M or Mobi eBook file does not exist' self.sbotton.configure(state='normal') return + # Head all Topaz ebooks off at the pass and warn user. + with open(mobipath, 'rb') as f: + raw = f.read() + if raw.startswith('TPZ'): + f.close() + tkMessageBox.showerror( + "K4MobiDeDRM", + "%s is a Topaz ebook. It cannot be decrypted with this tool. " + "You must use the Topaz Tools for this particular ebook." % mobipath) + self.status['text'] = 'The selected file is a Topaz ebook! Use Topaz tools.' + self.sbotton.configure(state='normal') + return + f.close() if not outpath: self.status['text'] = 'No output directory specified' self.sbotton.configure(state='normal') diff --git a/Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/lib/k4mobidedrm.py b/Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/lib/k4mobidedrm.py index df05f89..ee6ebd9 100644 --- a/Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/lib/k4mobidedrm.py +++ b/Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/lib/k4mobidedrm.py @@ -475,7 +475,7 @@ if not __name__ == "__main__" and inCalibre: Provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc.' supported_platforms = ['osx', 'windows', 'linux'] # Platforms this plugin will run on author = 'DiapDealer, SomeUpdates' # The author of this plugin - version = (0, 1, 2) # The version number of this plugin + version = (0, 1, 3) # The version number of this plugin file_types = set(['prc','mobi','azw']) # The file types that this plugin will be applied to on_import = True # Run this plugin during the import priority = 200 # run this plugin before mobidedrm, k4pcdedrm, k4dedrm @@ -483,6 +483,24 @@ if not __name__ == "__main__" and inCalibre: def run(self, path_to_ebook): from calibre.gui2 import is_ok_to_use_qt from PyQt4.Qt import QMessageBox + + # Head Topaz files off at the pass and warn the user that they will NOT + # be decrypted. Changes the file extension from .azw or .prc to .tpz so + # Calibre can at least read the metadata properly and the user can find + # them by sorting on 'format'. + with open(path_to_ebook, 'rb') as f: + raw = f.read() + if raw.startswith('TPZ'): + tf = self.temporary_file('.tpz') + if is_ok_to_use_qt(): + d = QMessageBox(QMessageBox.Warning, "K4MobiDeDRM Plugin", "%s is a Topaz book. It will NOT be decrypted!" % path_to_ebook) + d.show() + d.raise_() + d.exec_() + tf.write(raw) + tf.close + return tf.name + global kindleDatabase global openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap1, charMap2, charMap3, charMap4 if sys.platform.startswith('win'): diff --git a/Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/lib/mobidedrm.py b/Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/lib/mobidedrm.py index 9f17a3b..183432c 100644 --- a/Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/lib/mobidedrm.py +++ b/Kindle_Mobi_Tools/K4_Mobi_DeDRM_Combined_Tool/lib/mobidedrm.py @@ -39,8 +39,9 @@ # 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 - Corretion: It seems that multibyte entries are encrypted in a v6 file. -__version__ = '0.19' +__version__ = '0.20' import sys import struct @@ -208,8 +209,8 @@ class DrmStripper: if (mobi_length >= 0xE4) and (mobi_version >= 5): extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4]) print "Extra Data Flags = %d" %extra_data_flags - if mobi_version <= 5: - # multibyte utf8 data is included in the encryption for mobi_version 5 and below + if mobi_version < 7: + # multibyte utf8 data is included in the encryption for mobi_version 6 and below # so clear that byte so that we leave it to be decrypted. extra_data_flags &= 0xFFFE diff --git a/Kindle_Mobi_Tools/Kindle_4_Mac_Unswindle/lib/mobidedrm.py b/Kindle_Mobi_Tools/Kindle_4_Mac_Unswindle/lib/mobidedrm.py index 9f17a3b..183432c 100644 --- a/Kindle_Mobi_Tools/Kindle_4_Mac_Unswindle/lib/mobidedrm.py +++ b/Kindle_Mobi_Tools/Kindle_4_Mac_Unswindle/lib/mobidedrm.py @@ -39,8 +39,9 @@ # 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 - Corretion: It seems that multibyte entries are encrypted in a v6 file. -__version__ = '0.19' +__version__ = '0.20' import sys import struct @@ -208,8 +209,8 @@ class DrmStripper: if (mobi_length >= 0xE4) and (mobi_version >= 5): extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4]) print "Extra Data Flags = %d" %extra_data_flags - if mobi_version <= 5: - # multibyte utf8 data is included in the encryption for mobi_version 5 and below + if mobi_version < 7: + # multibyte utf8 data is included in the encryption for mobi_version 6 and below # so clear that byte so that we leave it to be decrypted. extra_data_flags &= 0xFFFE diff --git a/Kindle_Mobi_Tools/Kindle_4_PC_Unswindle/mobidedrm.py b/Kindle_Mobi_Tools/Kindle_4_PC_Unswindle/mobidedrm.py index 9f17a3b..183432c 100644 --- a/Kindle_Mobi_Tools/Kindle_4_PC_Unswindle/mobidedrm.py +++ b/Kindle_Mobi_Tools/Kindle_4_PC_Unswindle/mobidedrm.py @@ -39,8 +39,9 @@ # 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 - Corretion: It seems that multibyte entries are encrypted in a v6 file. -__version__ = '0.19' +__version__ = '0.20' import sys import struct @@ -208,8 +209,8 @@ class DrmStripper: if (mobi_length >= 0xE4) and (mobi_version >= 5): extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4]) print "Extra Data Flags = %d" %extra_data_flags - if mobi_version <= 5: - # multibyte utf8 data is included in the encryption for mobi_version 5 and below + if mobi_version < 7: + # multibyte utf8 data is included in the encryption for mobi_version 6 and below # so clear that byte so that we leave it to be decrypted. extra_data_flags &= 0xFFFE diff --git a/Kindle_Mobi_Tools/MobiDeDRM.py b/Kindle_Mobi_Tools/MobiDeDRM.py index 9f17a3b..183432c 100644 --- a/Kindle_Mobi_Tools/MobiDeDRM.py +++ b/Kindle_Mobi_Tools/MobiDeDRM.py @@ -39,8 +39,9 @@ # 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 - Corretion: It seems that multibyte entries are encrypted in a v6 file. -__version__ = '0.19' +__version__ = '0.20' import sys import struct @@ -208,8 +209,8 @@ class DrmStripper: if (mobi_length >= 0xE4) and (mobi_version >= 5): extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4]) print "Extra Data Flags = %d" %extra_data_flags - if mobi_version <= 5: - # multibyte utf8 data is included in the encryption for mobi_version 5 and below + if mobi_version < 7: + # multibyte utf8 data is included in the encryption for mobi_version 6 and below # so clear that byte so that we leave it to be decrypted. extra_data_flags &= 0xFFFE diff --git a/Mobi_Additional_Tools/lib/mobidedrm.py b/Mobi_Additional_Tools/lib/mobidedrm.py index 9f17a3b..183432c 100644 --- a/Mobi_Additional_Tools/lib/mobidedrm.py +++ b/Mobi_Additional_Tools/lib/mobidedrm.py @@ -39,8 +39,9 @@ # 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 - Corretion: It seems that multibyte entries are encrypted in a v6 file. -__version__ = '0.19' +__version__ = '0.20' import sys import struct @@ -208,8 +209,8 @@ class DrmStripper: if (mobi_length >= 0xE4) and (mobi_version >= 5): extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4]) print "Extra Data Flags = %d" %extra_data_flags - if mobi_version <= 5: - # multibyte utf8 data is included in the encryption for mobi_version 5 and below + if mobi_version < 7: + # multibyte utf8 data is included in the encryption for mobi_version 6 and below # so clear that byte so that we leave it to be decrypted. extra_data_flags &= 0xFFFE diff --git a/Topaz_Tools/FindTopazEbooks.pyw b/Topaz_Tools/FindTopazEbooks.pyw new file mode 100644 index 0000000..6a0df30 --- /dev/null +++ b/Topaz_Tools/FindTopazEbooks.pyw @@ -0,0 +1,216 @@ +#!/usr/bin/env python + +# This is a simple tool to identify all Amazon Topaz ebooks in a specific directory. +# There always seems to be confusion since Topaz books downloaded to K4PC/Mac can have +# almost any extension (.azw, .azw1, .prc, tpz). While the .azw1 and .tpz extensions +# are fairly easy to indentify, the others are not (without opening the files in an editor). + +# To run the tool with the GUI frontend, just double-click on the 'FindTopazFiles.pyw' file +# and select the folder where all of the ebooks in question are located. Then click 'Search'. +# The program will list the file names of the ebooks that are indentified as being Topaz. +# You can then isolate those books and use the Topaz tools to decrypt and convert them. + +# You can also run the script from a command line... supplying the folder to search +# as a parameter: python FindTopazEbooks.pyw "C:\My Folder" (change appropriately for +# your particular O.S.) + +# ** NOTE: This program does NOT decrypt or modify Topaz files in any way. It simply identifies them. + +# PLEASE DO NOT PIRATE EBOOKS! + +# We want all authors and publishers, and eBook stores to live +# long and prosperous lives but at the same time we just want to +# be able to read OUR books on whatever device we want and to keep +# readable for a long, long time + +# This borrows very heavily from works by CMBDTC, IHeartCabbages, skindle, +# unswindle, DarkReverser, ApprenticeAlf, DiapDealer, some_updates +# and many many others + +# Revision history: +# 1 - Initial release. + +from __future__ import with_statement + +__license__ = 'GPL v3' + +import sys +import os +import re +import shutil +import Tkinter +import Tkconstants +import tkFileDialog +import tkMessageBox + + +class ScrolledText(Tkinter.Text): + def __init__(self, master=None, **kw): + self.frame = Tkinter.Frame(master) + self.vbar = Tkinter.Scrollbar(self.frame) + self.vbar.pack(side=Tkconstants.RIGHT, fill=Tkconstants.Y) + kw.update({'yscrollcommand': self.vbar.set}) + Tkinter.Text.__init__(self, self.frame, **kw) + self.pack(side=Tkconstants.LEFT, fill=Tkconstants.BOTH, expand=True) + self.vbar['command'] = self.yview + # Copy geometry methods of self.frame without overriding Text + # methods = hack! + text_meths = vars(Tkinter.Text).keys() + methods = vars(Tkinter.Pack).keys() + vars(Tkinter.Grid).keys() + vars(Tkinter.Place).keys() + methods = set(methods).difference(text_meths) + for m in methods: + if m[0] != '_' and m != 'config' and m != 'configure': + setattr(self, m, getattr(self.frame, m)) + + def __str__(self): + return str(self.frame) + + +def cli_main(argv=sys.argv, obj=None): + progname = os.path.basename(argv[0]) + if len(argv) != 2: + print "usage: %s DIRECTORY" % (progname,) + return 1 + + if obj == None: + print "\nTopaz search results:\n" + else: + obj.stext.insert(Tkconstants.END,"Topaz search results:\n\n") + + inpath = argv[1] + files = os.listdir(inpath) + filefilter = re.compile("(\.azw$)|(\.azw1$)|(\.prc$)|(\.tpz$)", re.IGNORECASE) + files = filter(filefilter.search, files) + + if files: + topazcount = 0 + totalcount = 0 + for filename in files: + with open(os.path.join(inpath, filename), 'rb') as f: + try: + if f.read().startswith('TPZ'): + f.close() + basename, extension = os.path.splitext(filename) + if obj == None: + print " %s is a Topaz formatted ebook." % filename + """ + if extension == '.azw' or extension == '.prc': + print " renaming to %s" % (basename + '.tpz') + shutil.move(os.path.join(inpath, filename), + os.path.join(inpath, basename + '.tpz')) + """ + else: + msg1 = " %s is a Topaz formatted ebook.\n" % filename + obj.stext.insert(Tkconstants.END,msg1) + """ + if extension == '.azw' or extension == '.prc': + msg2 = " renaming to %s\n" % (basename + '.tpz') + obj.stext.insert(Tkconstants.END,msg2) + shutil.move(os.path.join(inpath, filename), + os.path.join(inpath, basename + '.tpz')) + """ + topazcount += 1 + except: + if obj == None: + print " Error reading %s." % filename + else: + msg = " Error reading or %s.\n" % filename + obj.stext.insert(Tkconstants.END,msg) + pass + totalcount += 1 + if topazcount == 0: + if obj == None: + print "\nNo Topaz books found in %s." % inpath + else: + msg = "\nNo Topaz books found in %s.\n\n" % inpath + obj.stext.insert(Tkconstants.END,msg) + else: + if obj == None: + print "\n%i Topaz books found in %s\n%i total books checked.\n" % (topazcount, inpath, totalcount) + else: + msg = "\n%i Topaz books found in %s\n%i total books checked.\n\n" %(topazcount, inpath, totalcount) + obj.stext.insert(Tkconstants.END,msg) + else: + if obj == None: + print "No typical Topaz file extensions found in %s.\n" % inpath + else: + msg = "No typical Topaz file extensions found in %s.\n\n" % inpath + obj.stext.insert(Tkconstants.END,msg) + + return 0 + + +class DecryptionDialog(Tkinter.Frame): + def __init__(self, root): + Tkinter.Frame.__init__(self, root, border=5) + ltext='Search a directory for Topaz eBooks\n' + self.status = Tkinter.Label(self, text=ltext) + self.status.pack(fill=Tkconstants.X, expand=1) + body = Tkinter.Frame(self) + body.pack(fill=Tkconstants.X, expand=1) + sticky = Tkconstants.E + Tkconstants.W + body.grid_columnconfigure(1, weight=2) + Tkinter.Label(body, text='Directory to Search').grid(row=1) + self.inpath = Tkinter.Entry(body, width=30) + self.inpath.grid(row=1, column=1, sticky=sticky) + button = Tkinter.Button(body, text="...", command=self.get_inpath) + button.grid(row=1, column=2) + msg1 = 'Topaz search results \n\n' + self.stext = ScrolledText(body, bd=5, relief=Tkconstants.RIDGE, + height=15, width=60, wrap=Tkconstants.WORD) + self.stext.grid(row=4, column=0, columnspan=2,sticky=sticky) + #self.stext.insert(Tkconstants.END,msg1) + buttons = Tkinter.Frame(self) + buttons.pack() + + + self.botton = Tkinter.Button( + buttons, text="Search", width=10, command=self.search) + self.botton.pack(side=Tkconstants.LEFT) + Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT) + self.button = Tkinter.Button( + buttons, text="Quit", width=10, command=self.quit) + self.button.pack(side=Tkconstants.RIGHT) + + def get_inpath(self): + cwd = os.getcwdu() + cwd = cwd.encode('utf-8') + inpath = tkFileDialog.askdirectory( + parent=None, title='Directory to search', + initialdir=cwd, initialfile=None) + if inpath: + inpath = os.path.normpath(inpath) + self.inpath.delete(0, Tkconstants.END) + self.inpath.insert(0, inpath) + return + + + def search(self): + inpath = self.inpath.get() + if not inpath or not os.path.exists(inpath): + self.status['text'] = 'Specified directory does not exist' + return + argv = [sys.argv[0], inpath] + self.status['text'] = 'Searching...' + self.botton.configure(state='disabled') + cli_main(argv, self) + self.status['text'] = 'Search a directory for Topaz files' + self.botton.configure(state='normal') + + return + + +def gui_main(): + root = Tkinter.Tk() + root.title('Topaz eBook Finder') + root.resizable(True, False) + root.minsize(370, 0) + DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1) + root.mainloop() + return 0 + + +if __name__ == '__main__': + if len(sys.argv) > 1: + sys.exit(cli_main()) + sys.exit(gui_main()) \ No newline at end of file