mirror of
https://github.com/noDRM/DeDRM_tools
synced 2024-12-28 22:24:27 +01:00
tools v2.4
This commit is contained in:
parent
9162698f89
commit
38eabe7612
23 changed files with 591 additions and 87 deletions
Binary file not shown.
|
@ -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)
|
||||||
|
|
|
@ -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.'
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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'):
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Binary file not shown.
|
@ -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
|
||||||
|
|
||||||
|
|
216
Kindle_Mobi_Tools/FindTopazEbooks.pyw
Normal file
216
Kindle_Mobi_Tools/FindTopazEbooks.pyw
Normal 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())
|
|
@ -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')
|
||||||
|
|
|
@ -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'):
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
216
Topaz_Tools/FindTopazEbooks.pyw
Normal file
216
Topaz_Tools/FindTopazEbooks.pyw
Normal 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())
|
Loading…
Reference in a new issue