tools v2.4

This commit is contained in:
Apprentice Alf 2010-12-15 21:21:51 +00:00
parent 9162698f89
commit 38eabe7612
23 changed files with 591 additions and 87 deletions

Binary file not shown.

View file

@ -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)

View file

@ -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.'

View file

@ -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'):

View file

@ -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

View file

@ -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
<http://unlicense.org/>
" 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

View file

@ -24,7 +24,7 @@
<key>CFBundleExecutable</key>
<string>droplet</string>
<key>CFBundleGetInfoString</key>
<string>DeDRM 1.2, Copyright © 2010 by Apprentice Alf.</string>
<string>DeDRM 1.3, Copyright © 2010 by Apprentice Alf.</string>
<key>CFBundleIconFile</key>
<string>droplet</string>
<key>CFBundleInfoDictionaryVersion</key>
@ -34,7 +34,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.2</string>
<string>1.3</string>
<key>CFBundleSignature</key>
<string>dplt</string>
<key>LSMinimumSystemVersion</key>
@ -46,9 +46,9 @@
<key>name</key>
<string>ScriptWindowState</string>
<key>positionOfDivider</key>
<real>885</real>
<real>739</real>
<key>savedFrame</key>
<string>1507 -64 1262 964 1440 -150 1680 1050 </string>
<string>1533 -24 1262 818 1440 -150 1680 1050 </string>
<key>selectedTabView</key>
<string>result</string>
</dict>

View file

@ -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

View file

@ -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())

View file

@ -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')

View file

@ -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'):

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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())