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: else:
import xml.etree.ElementTree as etree import xml.etree.ElementTree as etree
import Carbon.File import subprocess
import Carbon.Folder
import Carbon.Folders
import MacOS
ACTIVATION_PATH = 'Adobe/Digital Editions/activation.dat'
NSMAP = {'adept': 'http://ns.adobe.com/adept', NSMAP = {'adept': 'http://ns.adobe.com/adept',
'enc': 'http://www.w3.org/2001/04/xmlenc#'} 'enc': 'http://www.w3.org/2001/04/xmlenc#'}
def find_folder(domain, dtype): def findActivationDat():
try: home = os.getenv('HOME')
fsref = Carbon.Folder.FSFindFolder(domain, dtype, False) cmdline = 'find "' + home + '/Library/Application Support/Adobe/Digital Editions" -name "activation.dat"'
return Carbon.File.pathname(fsref) cmdline = cmdline.encode(sys.getfilesystemencoding())
except MacOS.Error: p2 = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
return None out1, out2 = p2.communicate()
reslst = out1.split('\n')
def find_app_support_file(subpath): cnt = len(reslst)
dtype = Carbon.Folders.kApplicationSupportFolderType for j in xrange(cnt):
for domain in Carbon.Folders.kUserDomain, Carbon.Folders.kLocalDomain: resline = reslst[j]
path = find_folder(domain, dtype) pp = resline.find('activation.dat')
if path is None: if pp >= 0:
continue ActDatPath = resline
path = os.path.join(path, subpath) break
if os.path.isfile(path): if os.path.exists(ActDatPath):
return path return ActDatPath
return None return None
def retrieve_key(): def retrieve_key():
actpath = find_app_support_file(ACTIVATION_PATH) actpath = findActivationDat()
if actpath is None: if actpath is None:
raise ADEPTError("Could not locate ADE activation") raise ADEPTError("Could not locate ADE activation")
tree = etree.parse(actpath) tree = etree.parse(actpath)

View File

@ -42,7 +42,9 @@
# Revision history: # Revision history:
# 0.1 - Initial release # 0.1 - Initial release
# 0.1.1 - Allow Windows users to make use of openssl if they have it installed. # 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.' Credit given to I <3 Cabbages for the original stand-alone scripts.'
supported_platforms = ['linux', 'osx', 'windows'] supported_platforms = ['linux', 'osx', 'windows']
author = 'DiapDealer' author = 'DiapDealer'
version = (0, 1, 1) version = (0, 1, 2)
minimum_calibre_version = (0, 6, 44) # Compiled python libraries cannot be imported in earlier versions. minimum_calibre_version = (0, 6, 44) # Compiled python libraries cannot be imported in earlier versions.
file_types = set(['epub']) file_types = set(['epub'])
on_import = True on_import = True
@ -420,7 +422,7 @@ class IneptDeDRM(FileTypePlugin):
try: try:
keydata = retrieve_key() keydata = retrieve_key()
userkeys.append(keydata) userkeys.append(keydata)
keypath = os.path.join(confpath, 'adeptkey.der') keypath = os.path.join(confpath, 'calibre-adeptkey.der')
with open(keypath, 'wb') as f: with open(keypath, 'wb') as f:
f.write(keydata) f.write(keydata)
print 'IneptEpub: Created keyfile from ADE install.' 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.' 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 supported_platforms = ['osx', 'windows', 'linux'] # Platforms this plugin will run on
author = 'DiapDealer, SomeUpdates' # The author of this plugin 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 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 on_import = True # Run this plugin during the import
priority = 200 # run this plugin before mobidedrm, k4pcdedrm, k4dedrm 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): def run(self, path_to_ebook):
from calibre.gui2 import is_ok_to_use_qt from calibre.gui2 import is_ok_to_use_qt
from PyQt4.Qt import QMessageBox 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 kindleDatabase
global openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap1, charMap2, charMap3, charMap4 global openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap1, charMap2, charMap3, charMap4
if sys.platform.startswith('win'): if sys.platform.startswith('win'):

View File

@ -39,8 +39,9 @@
# Removed the disabled Calibre plug-in code # Removed the disabled Calibre plug-in code
# Permit use of 8-digit PIDs # Permit use of 8-digit PIDs
# 0.19 - It seems that multibyte entries aren't encrypted in a v6 file either. # 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 sys
import struct import struct
@ -208,8 +209,8 @@ class DrmStripper:
if (mobi_length >= 0xE4) and (mobi_version >= 5): if (mobi_length >= 0xE4) and (mobi_version >= 5):
extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4]) extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4])
print "Extra Data Flags = %d" %extra_data_flags print "Extra Data Flags = %d" %extra_data_flags
if mobi_version <= 5: if mobi_version < 7:
# multibyte utf8 data is included in the encryption for mobi_version 5 and below # 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. # so clear that byte so that we leave it to be decrypted.
extra_data_flags &= 0xFFFE 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 sourcePath to (POSIX path of file parent_folder) & fileName & "_Source/"
set pmlzFilePath to POSIX path of file parent_folder & fileName & "_dedrmed.pmlz" set pmlzFilePath to POSIX path of file parent_folder & fileName & "_dedrmed.pmlz"
if length of bnKeys is 0 then if length of bnKeys is 0 then
GetKeys() GetKeys(false)
end if end if
set shellresult to "Error: No eReader/Barnes & Noble Name:Number keys supplied. Can't decrypt." set shellresult to "Error: No eReader/Barnes & Noble Name:Number keys supplied. Can't decrypt."
repeat with BNKey in bnKeys repeat with BNKey in bnKeys
@ -367,7 +367,7 @@ end unlockepubfile
on unlockpdffile(encryptedFile) on unlockpdffile(encryptedFile)
--check it's an ePub file. --check it's an ePub file.
set PDFSig to "NOT_DF" set PDFSig to "NOT_PDF"
try try
set PDFSig to read file encryptedFile from 1 for 4 set PDFSig to read file encryptedFile from 1 for 4
end try end try
@ -393,23 +393,7 @@ on unlockpdffile(encryptedFile)
set decoded to "NO" set decoded to "NO"
-- first we must check we have a PDF script -- first we must check we have a PDF script
if not fileexists(AdobePDFTool) then GetIneptPDF(false)
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
if not fileexists(AdobePDFTool) then if not fileexists(AdobePDFTool) then
set ErrorCount to ErrorCount + 1 set ErrorCount to ErrorCount + 1
set ErrorList to ErrorList & encryptedFile & " is a PDF file and no ineptpdf script found. 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:" Enter any additional Mobipocket PIDs for your Mobipocket books one at a time:"
set FinishedButton to "No More" set FinishedButton to "No More"
end if 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 if button returned of dialogresult is "Add" then
set PID to text returned of dialogresult set PID to text returned of dialogresult
set PIDlength to length of PID set PIDlength to length of PID
@ -657,7 +641,37 @@ To add extra Kindle Info files, click the Add
end repeat end repeat
end GetKindleInfoFiles 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 "" set bnKeyText to ""
repeat repeat
set BNKeystring to GetBNKeystring() set BNKeystring to GetBNKeystring()
@ -671,9 +685,12 @@ on GetKeys()
Please enter any additional " Please enter any additional "
set FinishedButton to "No More" set FinishedButton to "No More"
end if 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 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"
set dialogresult to (display dialog DialogPrompt default answer bnKeyText buttons {"Delete All", "Add", FinishedButton} with title "DeDRM Applescript" default button 2) 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 if button returned of dialogresult is "Add" then
set bnKeyText to text returned of dialogresult set bnKeyText to text returned of dialogresult
if bnKeyText is "" then if bnKeyText is "" then
@ -686,17 +703,17 @@ Please enter any additional "
display dialog message display dialog message
end if end if
end try end try
else if not (bnKeyText contains ":") then 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 display dialog "Name and Number must be separated by a comma (,)." buttons {"OK"} default button 1 with title "DeDRM Applescript" with icon caution
else 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 keyNumber to the last text item of bnKeyText
set keyName to (text items 1 through -2 of bnKeyText) as string 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 shellresult to ""
set keyfilepath 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 -- get the B&N key from this pair
set shellresult to "no result" set shellresult to "no result"
set scriptError to "Key Gen Script failed." 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 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 end if
else 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
end if end if
else if button returned of dialogresult is "Delete All" then 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." To add extra key files (.der), click the AddÉ button."
set FinishedButton to "No More" set FinishedButton to "No More"
end if 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 if button returned of dialogresult is "AddÉ" then
try try
set newFile to (choose file with prompt "Please select an Adept key file") as text 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 For more information, please refer to
<http://unlicense.org/> <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() ReadPrefs()
GetPIDs() GetPIDs()
GetKeys() GetKeys(true)
GetAdeptKey(true) GetAdeptKey(true)
GetAdeptKeyFiles() GetAdeptKeyFiles()
GetIneptPDF(true)
--GetKindleInfoFiles() --GetKindleInfoFiles()
WritePrefs() WritePrefs()
end if end if

View File

@ -24,7 +24,7 @@
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>droplet</string> <string>droplet</string>
<key>CFBundleGetInfoString</key> <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> <key>CFBundleIconFile</key>
<string>droplet</string> <string>droplet</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
@ -34,7 +34,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.2</string> <string>1.3</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>dplt</string> <string>dplt</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
@ -46,9 +46,9 @@
<key>name</key> <key>name</key>
<string>ScriptWindowState</string> <string>ScriptWindowState</string>
<key>positionOfDivider</key> <key>positionOfDivider</key>
<real>885</real> <real>739</real>
<key>savedFrame</key> <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> <key>selectedTabView</key>
<string>result</string> <string>result</string>
</dict> </dict>

View File

@ -39,8 +39,9 @@
# Removed the disabled Calibre plug-in code # Removed the disabled Calibre plug-in code
# Permit use of 8-digit PIDs # Permit use of 8-digit PIDs
# 0.19 - It seems that multibyte entries aren't encrypted in a v6 file either. # 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 sys
import struct import struct
@ -208,8 +209,8 @@ class DrmStripper:
if (mobi_length >= 0xE4) and (mobi_version >= 5): if (mobi_length >= 0xE4) and (mobi_version >= 5):
extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4]) extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4])
print "Extra Data Flags = %d" %extra_data_flags print "Extra Data Flags = %d" %extra_data_flags
if mobi_version <= 5: if mobi_version < 7:
# multibyte utf8 data is included in the encryption for mobi_version 5 and below # 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. # so clear that byte so that we leave it to be decrypted.
extra_data_flags &= 0xFFFE 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.status['text'] = 'Specified K4PC, K4M or Mobi eBook file does not exist'
self.sbotton.configure(state='normal') self.sbotton.configure(state='normal')
return 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: if not outpath:
self.status['text'] = 'No output directory specified' self.status['text'] = 'No output directory specified'
self.sbotton.configure(state='normal') 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.' 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 supported_platforms = ['osx', 'windows', 'linux'] # Platforms this plugin will run on
author = 'DiapDealer, SomeUpdates' # The author of this plugin 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 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 on_import = True # Run this plugin during the import
priority = 200 # run this plugin before mobidedrm, k4pcdedrm, k4dedrm 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): def run(self, path_to_ebook):
from calibre.gui2 import is_ok_to_use_qt from calibre.gui2 import is_ok_to_use_qt
from PyQt4.Qt import QMessageBox 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 kindleDatabase
global openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap1, charMap2, charMap3, charMap4 global openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap1, charMap2, charMap3, charMap4
if sys.platform.startswith('win'): if sys.platform.startswith('win'):

View File

@ -39,8 +39,9 @@
# Removed the disabled Calibre plug-in code # Removed the disabled Calibre plug-in code
# Permit use of 8-digit PIDs # Permit use of 8-digit PIDs
# 0.19 - It seems that multibyte entries aren't encrypted in a v6 file either. # 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 sys
import struct import struct
@ -208,8 +209,8 @@ class DrmStripper:
if (mobi_length >= 0xE4) and (mobi_version >= 5): if (mobi_length >= 0xE4) and (mobi_version >= 5):
extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4]) extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4])
print "Extra Data Flags = %d" %extra_data_flags print "Extra Data Flags = %d" %extra_data_flags
if mobi_version <= 5: if mobi_version < 7:
# multibyte utf8 data is included in the encryption for mobi_version 5 and below # 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. # so clear that byte so that we leave it to be decrypted.
extra_data_flags &= 0xFFFE extra_data_flags &= 0xFFFE

View File

@ -39,8 +39,9 @@
# Removed the disabled Calibre plug-in code # Removed the disabled Calibre plug-in code
# Permit use of 8-digit PIDs # Permit use of 8-digit PIDs
# 0.19 - It seems that multibyte entries aren't encrypted in a v6 file either. # 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 sys
import struct import struct
@ -208,8 +209,8 @@ class DrmStripper:
if (mobi_length >= 0xE4) and (mobi_version >= 5): if (mobi_length >= 0xE4) and (mobi_version >= 5):
extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4]) extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4])
print "Extra Data Flags = %d" %extra_data_flags print "Extra Data Flags = %d" %extra_data_flags
if mobi_version <= 5: if mobi_version < 7:
# multibyte utf8 data is included in the encryption for mobi_version 5 and below # 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. # so clear that byte so that we leave it to be decrypted.
extra_data_flags &= 0xFFFE extra_data_flags &= 0xFFFE

View File

@ -39,8 +39,9 @@
# Removed the disabled Calibre plug-in code # Removed the disabled Calibre plug-in code
# Permit use of 8-digit PIDs # Permit use of 8-digit PIDs
# 0.19 - It seems that multibyte entries aren't encrypted in a v6 file either. # 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 sys
import struct import struct
@ -208,8 +209,8 @@ class DrmStripper:
if (mobi_length >= 0xE4) and (mobi_version >= 5): if (mobi_length >= 0xE4) and (mobi_version >= 5):
extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4]) extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4])
print "Extra Data Flags = %d" %extra_data_flags print "Extra Data Flags = %d" %extra_data_flags
if mobi_version <= 5: if mobi_version < 7:
# multibyte utf8 data is included in the encryption for mobi_version 5 and below # 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. # so clear that byte so that we leave it to be decrypted.
extra_data_flags &= 0xFFFE extra_data_flags &= 0xFFFE

View File

@ -39,8 +39,9 @@
# Removed the disabled Calibre plug-in code # Removed the disabled Calibre plug-in code
# Permit use of 8-digit PIDs # Permit use of 8-digit PIDs
# 0.19 - It seems that multibyte entries aren't encrypted in a v6 file either. # 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 sys
import struct import struct
@ -208,8 +209,8 @@ class DrmStripper:
if (mobi_length >= 0xE4) and (mobi_version >= 5): if (mobi_length >= 0xE4) and (mobi_version >= 5):
extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4]) extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4])
print "Extra Data Flags = %d" %extra_data_flags print "Extra Data Flags = %d" %extra_data_flags
if mobi_version <= 5: if mobi_version < 7:
# multibyte utf8 data is included in the encryption for mobi_version 5 and below # 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. # so clear that byte so that we leave it to be decrypted.
extra_data_flags &= 0xFFFE extra_data_flags &= 0xFFFE

View File

@ -39,8 +39,9 @@
# Removed the disabled Calibre plug-in code # Removed the disabled Calibre plug-in code
# Permit use of 8-digit PIDs # Permit use of 8-digit PIDs
# 0.19 - It seems that multibyte entries aren't encrypted in a v6 file either. # 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 sys
import struct import struct
@ -208,8 +209,8 @@ class DrmStripper:
if (mobi_length >= 0xE4) and (mobi_version >= 5): if (mobi_length >= 0xE4) and (mobi_version >= 5):
extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4]) extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4])
print "Extra Data Flags = %d" %extra_data_flags print "Extra Data Flags = %d" %extra_data_flags
if mobi_version <= 5: if mobi_version < 7:
# multibyte utf8 data is included in the encryption for mobi_version 5 and below # 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. # so clear that byte so that we leave it to be decrypted.
extra_data_flags &= 0xFFFE 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())