tools v4.7

pull/6/head v4.7
Apprentice Alf 13 years ago
parent ba5927a20d
commit e95ed1a8ed

@ -16,24 +16,23 @@ import re
class K4DeDRM(FileTypePlugin): class K4DeDRM(FileTypePlugin):
name = 'K4PC, K4Mac, Kindle Mobi and Topaz DeDRM' # Name of the plugin name = 'K4PC, K4Mac, Kindle Mobi and Topaz DeDRM' # Name of the plugin
description = 'Removes DRM from K4PC and Mac, Kindle Mobi and Topaz files. Provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc.' description = 'Removes DRM from Mobipocket, Kindle/Mobi, Kindle/Topaz and Kindle/Print Replica files. Provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc.'
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, 3, 6) # The version number of this plugin version = (0, 3, 7) # The version number of this plugin
file_types = set(['prc','mobi','azw','azw1','tpz']) # The file types that this plugin will be applied to file_types = set(['prc','mobi','azw','azw1','azw4','tpz']) # 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 = 210 # run this plugin before mobidedrm, k4pcdedrm, k4dedrm priority = 210 # run this plugin before mobidedrm, k4pcdedrm, k4dedrm
minimum_calibre_version = (0, 7, 55) minimum_calibre_version = (0, 7, 55)
def run(self, path_to_ebook): def run(self, path_to_ebook):
plug_ver = '.'.join(str(self.version).strip('()').replace(' ', '').split(','))
k4 = True k4 = True
if sys.platform.startswith('linux'): if sys.platform.startswith('linux'):
k4 = False k4 = False
pids = [] pids = []
serials = [] serials = []
kInfoFiles = [] kInfoFiles = []
# Get supplied list of PIDs to try from plugin customization. # Get supplied list of PIDs to try from plugin customization.
customvalues = self.site_customization.split(',') customvalues = self.site_customization.split(',')
for customvalue in customvalues: for customvalue in customvalues:
@ -46,12 +45,12 @@ class K4DeDRM(FileTypePlugin):
serials.append(customvalue) serials.append(customvalue)
else: else:
print "%s is not a valid Kindle serial number or PID." % str(customvalue) print "%s is not a valid Kindle serial number or PID." % str(customvalue)
# Load any kindle info files (*.info) included Calibre's config directory. # Load any kindle info files (*.info) included Calibre's config directory.
try: try:
# Find Calibre's configuration directory. # Find Calibre's configuration directory.
confpath = os.path.split(os.path.split(self.plugin_path)[0])[0] confpath = os.path.split(os.path.split(self.plugin_path)[0])[0]
print 'K4MobiDeDRM: Calibre configuration directory = %s' % confpath print 'K4MobiDeDRM v%s: Calibre configuration directory = %s' % (plug_ver, confpath)
files = os.listdir(confpath) files = os.listdir(confpath)
filefilter = re.compile("\.info$|\.kinf$", re.IGNORECASE) filefilter = re.compile("\.info$|\.kinf$", re.IGNORECASE)
files = filter(filefilter.search, files) files = filter(filefilter.search, files)
@ -59,9 +58,9 @@ class K4DeDRM(FileTypePlugin):
for filename in files: for filename in files:
fpath = os.path.join(confpath, filename) fpath = os.path.join(confpath, filename)
kInfoFiles.append(fpath) kInfoFiles.append(fpath)
print 'K4MobiDeDRM: Kindle info/kinf file %s found in config folder.' % filename print 'K4MobiDeDRM v%s: Kindle info/kinf file %s found in config folder.' % (plug_ver, filename)
except IOError: except IOError:
print 'K4MobiDeDRM: Error reading kindle info/kinf files from config directory.' print 'K4MobiDeDRM v%s: Error reading kindle info/kinf files from config directory.' % plug_ver
pass pass
mobi = True mobi = True
@ -83,30 +82,34 @@ class K4DeDRM(FileTypePlugin):
try: try:
mb.processBook(pidlst) mb.processBook(pidlst)
except mobidedrm.DrmException: except mobidedrm.DrmException, e:
#if you reached here then no luck raise and exception #if you reached here then no luck raise and exception
if is_ok_to_use_qt(): if is_ok_to_use_qt():
from PyQt4.Qt import QMessageBox from PyQt4.Qt import QMessageBox
d = QMessageBox(QMessageBox.Warning, "K4MobiDeDRM Plugin", "Error decoding: %s\n" % path_to_ebook) d = QMessageBox(QMessageBox.Warning, "K4MobiDeDRM v%s Plugin" % plug_ver, "Error: " + str(e) + "... %s\n" % path_to_ebook)
d.show() d.show()
d.raise_() d.raise_()
d.exec_() d.exec_()
raise Exception("K4MobiDeDRM plugin could not decode the file") raise Exception("K4MobiDeDRM plugin v%s Error: %s" % (plug_ver, str(e)))
except topazextract.TpzDRMError: except topazextract.TpzDRMError, e:
#if you reached here then no luck raise and exception #if you reached here then no luck raise and exception
if is_ok_to_use_qt(): if is_ok_to_use_qt():
from PyQt4.Qt import QMessageBox from PyQt4.Qt import QMessageBox
d = QMessageBox(QMessageBox.Warning, "K4MobiDeDRM Plugin", "Error decoding: %s\n" % path_to_ebook) d = QMessageBox(QMessageBox.Warning, "K4MobiDeDRM v%s Plugin" % plug_ver, "Error: " + str(e) + "... %s\n" % path_to_ebook)
d.show() d.show()
d.raise_() d.raise_()
d.exec_() d.exec_()
raise Exception("K4MobiDeDRM plugin could not decode the file") raise Exception("K4MobiDeDRM plugin v%s Error: %s" % (plug_ver, str(e)))
print "Success!" print "Success!"
if mobi: if mobi:
of = self.temporary_file(bookname+'.mobi') if mb.getPrintReplica():
of = self.temporary_file(bookname+'.azw4')
print 'K4MobiDeDRM v%s: Print Replica format detected.' % plug_ver
else:
of = self.temporary_file(bookname+'.mobi')
mb.getMobiFile(of.name) mb.getMobiFile(of.name)
else : else:
of = self.temporary_file(bookname+'.htmlz') of = self.temporary_file(bookname+'.htmlz')
mb.getHTMLZip(of.name) mb.getHTMLZip(of.name)
mb.cleanup() mb.cleanup()

@ -17,7 +17,7 @@ from __future__ import with_statement
# and many many others # and many many others
__version__ = '3.6' __version__ = '3.7'
class Unbuffered: class Unbuffered:
def __init__(self, stream): def __init__(self, stream):
@ -76,7 +76,7 @@ def cleanup_name(name):
def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids): def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
# handle the obvious cases at the beginning # handle the obvious cases at the beginning
if not os.path.isfile(infile): if not os.path.isfile(infile):
print "Error: Input file does not exist" print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: Input file does not exist"
return 1 return 1
mobi = True mobi = True
@ -106,17 +106,20 @@ def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
mb.processBook(pidlst) mb.processBook(pidlst)
except mobidedrm.DrmException, e: except mobidedrm.DrmException, e:
print "Error: " + str(e) + "\nDRM Removal Failed.\n" print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n"
return 1 return 1
except topazextract.TpzDRMError, e: except topazextract.TpzDRMError, e:
print "Error: " + str(e) + "\nDRM Removal Failed.\n" print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n"
return 1 return 1
except Exception, e: except Exception, e:
print "Error: " + str(e) + "\nDRM Removal Failed.\n" print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n"
return 1 return 1
if mobi: if mobi:
outfile = os.path.join(outdir, outfilename + '_nodrm' + '.mobi') if mb.getPrintReplica():
outfile = os.path.join(outdir, outfilename + '_nodrm' + '.azw4')
else:
outfile = os.path.join(outdir, outfilename + '_nodrm' + '.mobi')
mb.getMobiFile(outfile) mb.getMobiFile(outfile)
return 0 return 0
@ -158,7 +161,6 @@ def main(argv=sys.argv):
print ('K4MobiDeDrm v%(__version__)s ' print ('K4MobiDeDrm v%(__version__)s '
'provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc .' % globals()) 'provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc .' % globals())
print ' '
try: try:
opts, args = getopt.getopt(sys.argv[1:], "k:p:s:") opts, args = getopt.getopt(sys.argv[1:], "k:p:s:")
except getopt.GetoptError, err: except getopt.GetoptError, err:

@ -53,8 +53,9 @@
# files, but they are not for HUFF/CDIC compress files! # files, but they are not for HUFF/CDIC compress files!
# 0.30 - Modified interface slightly to work better with new calibre plugin style # 0.30 - Modified interface slightly to work better with new calibre plugin style
# 0.31 - The multibyte encrytion info is true for version 7 files too. # 0.31 - The multibyte encrytion info is true for version 7 files too.
# 0.32 - Added support for "Print Replica" Kindle ebooks
__version__ = '0.31' __version__ = '0.32'
import sys import sys
@ -163,6 +164,9 @@ class MobiBook:
return self.data_file[off:endoff] return self.data_file[off:endoff]
def __init__(self, infile): def __init__(self, infile):
print ('MobiDeDrm v%(__version__)s. '
'Copyright 2008-2011 The Dark Reverser et al.' % globals())
# initial sanity check on file # initial sanity check on file
self.data_file = file(infile, 'rb').read() self.data_file = file(infile, 'rb').read()
self.mobi_data = '' self.mobi_data = ''
@ -193,6 +197,7 @@ class MobiBook:
self.meta_array = {} self.meta_array = {}
return return
self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18]) self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18])
self.mobi_codepage, = struct.unpack('>L',self.sect[0x1c:0x20])
self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C]) self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C])
print "MOBI header version = %d, length = %d" %(self.mobi_version, self.mobi_length) print "MOBI header version = %d, length = %d" %(self.mobi_version, self.mobi_length)
self.extra_data_flags = 0 self.extra_data_flags = 0
@ -230,8 +235,13 @@ class MobiBook:
except: except:
self.meta_array = {} self.meta_array = {}
pass pass
self.print_replica = False
def getBookTitle(self): def getBookTitle(self):
codec_map = {
1252 : 'windows-1252',
65001 : 'utf-8',
}
title = '' title = ''
if 503 in self.meta_array: if 503 in self.meta_array:
title = self.meta_array[503] title = self.meta_array[503]
@ -242,7 +252,10 @@ class MobiBook:
if title == '': if title == '':
title = self.header[:32] title = self.header[:32]
title = title.split("\0")[0] title = title.split("\0")[0]
return title codec = 'windows-1252'
if self.mobi_codepage in codec_map.keys():
codec = codec_map[self.mobi_codepage]
return unicode(title, codec).encode('utf-8')
def getPIDMetaInfo(self): def getPIDMetaInfo(self):
rec209 = '' rec209 = ''
@ -306,6 +319,9 @@ class MobiBook:
def getMobiFile(self, outpath): def getMobiFile(self, outpath):
file(outpath,'wb').write(self.mobi_data) file(outpath,'wb').write(self.mobi_data)
def getPrintReplica(self):
return self.print_replica
def processBook(self, pidlist): def processBook(self, pidlist):
crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2]) crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2])
@ -313,10 +329,17 @@ class MobiBook:
self.crypto_type = crypto_type self.crypto_type = crypto_type
if crypto_type == 0: if crypto_type == 0:
print "This book is not encrypted." print "This book is not encrypted."
# we must still check for Print Replica
self.print_replica = (self.loadSection(1)[0:4] == '%MOP')
self.mobi_data = self.data_file self.mobi_data = self.data_file
return return
if crypto_type != 2 and crypto_type != 1: if crypto_type != 2 and crypto_type != 1:
raise DrmException("Cannot decode unknown Mobipocket encryption type %d" % crypto_type) raise DrmException("Cannot decode unknown Mobipocket encryption type %d" % crypto_type)
if 406 in self.meta_array:
data406 = self.meta_array[406]
val406, = struct.unpack('>Q',data406)
if val406 != 0:
raise DrmException("Cannot decode library or rented ebooks.")
goodpids = [] goodpids = []
for pid in pidlist: for pid in pidlist:
@ -367,7 +390,10 @@ class MobiBook:
if i%100 == 0: if i%100 == 0:
print ".", print ".",
# print "record %d, extra_size %d" %(i,extra_size) # print "record %d, extra_size %d" %(i,extra_size)
self.mobi_data += PC1(found_key, data[0:len(data) - extra_size]) decoded_data = PC1(found_key, data[0:len(data) - extra_size])
if i==1:
self.print_replica = (decoded_data[0:4] == '%MOP')
self.mobi_data += decoded_data
if extra_size > 0: if extra_size > 0:
self.mobi_data += data[-extra_size:] self.mobi_data += data[-extra_size:]
if self.num_sections > self.records+1: if self.num_sections > self.records+1:
@ -392,9 +418,9 @@ def getUnencryptedBookWithList(infile,pidlist):
def main(argv=sys.argv): def main(argv=sys.argv):
print ('MobiDeDrm v%(__version__)s. ' print ('MobiDeDrm v%(__version__)s. '
'Copyright 2008-2010 The Dark Reverser.' % globals()) 'Copyright 2008-2011 The Dark Reverser et al.' % globals())
if len(argv)<3 or len(argv)>4: if len(argv)<3 or len(argv)>4:
print "Removes protection from Mobipocket books" print "Removes protection from Kindle/Mobipocket and Kindle/Print Replica ebooks"
print "Usage:" print "Usage:"
print " %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0] print " %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0]
return 1 return 1
@ -402,9 +428,9 @@ def main(argv=sys.argv):
infile = argv[1] infile = argv[1]
outfile = argv[2] outfile = argv[2]
if len(argv) is 4: if len(argv) is 4:
pidlist = argv[3].split(',') pidlist = argv[3].split(',')
else: else:
pidlist = {} pidlist = {}
try: try:
stripped_file = getUnencryptedBookWithList(infile, pidlist) stripped_file = getUnencryptedBookWithList(infile, pidlist)
file(outfile, 'wb').write(stripped_file) file(outfile, 'wb').write(stripped_file)

@ -24,17 +24,17 @@
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>droplet</string> <string>droplet</string>
<key>CFBundleGetInfoString</key> <key>CFBundleGetInfoString</key>
<string>DeDRM 2.9, Written 20102011 by Apprentice Alf and others.</string> <string>DeDRM 3.0, Written 20102011 by Apprentice Alf and others.</string>
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
<string>droplet</string> <string>droplet</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>DeDRM</string> <string>DeDRM 3.0</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>2.9</string> <string>3.0</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>dplt</string> <string>dplt</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
@ -43,18 +43,14 @@
<true/> <true/>
<key>WindowState</key> <key>WindowState</key>
<dict> <dict>
<key>dividerCollapsed</key>
<true/>
<key>eventLogLevel</key>
<integer>-1</integer>
<key>name</key> <key>name</key>
<string>ScriptWindowState</string> <string>ScriptWindowState</string>
<key>positionOfDivider</key> <key>positionOfDivider</key>
<real>0</real> <real>274</real>
<key>savedFrame</key> <key>savedFrame</key>
<string>1578 27 862 788 1440 -150 1680 1050 </string> <string>39 376 439 476 0 0 1440 878 </string>
<key>selectedTabView</key> <key>selectedTabView</key>
<string>event log</string> <string>result</string>
</dict> </dict>
</dict> </dict>
</plist> </plist>

@ -1,4 +1,4 @@
{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350 {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf540
{\fonttbl} {\fonttbl}
{\colortbl;\red255\green255\blue255;} {\colortbl;\red255\green255\blue255;}
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 B

After

Width:  |  Height:  |  Size: 362 B

@ -17,7 +17,7 @@ from __future__ import with_statement
# and many many others # and many many others
__version__ = '3.6' __version__ = '3.7'
class Unbuffered: class Unbuffered:
def __init__(self, stream): def __init__(self, stream):
@ -76,7 +76,7 @@ def cleanup_name(name):
def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids): def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
# handle the obvious cases at the beginning # handle the obvious cases at the beginning
if not os.path.isfile(infile): if not os.path.isfile(infile):
print "Error: Input file does not exist" print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: Input file does not exist"
return 1 return 1
mobi = True mobi = True
@ -106,17 +106,20 @@ def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
mb.processBook(pidlst) mb.processBook(pidlst)
except mobidedrm.DrmException, e: except mobidedrm.DrmException, e:
print "Error: " + str(e) + "\nDRM Removal Failed.\n" print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n"
return 1 return 1
except topazextract.TpzDRMError, e: except topazextract.TpzDRMError, e:
print "Error: " + str(e) + "\nDRM Removal Failed.\n" print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n"
return 1 return 1
except Exception, e: except Exception, e:
print "Error: " + str(e) + "\nDRM Removal Failed.\n" print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n"
return 1 return 1
if mobi: if mobi:
outfile = os.path.join(outdir, outfilename + '_nodrm' + '.mobi') if mb.getPrintReplica():
outfile = os.path.join(outdir, outfilename + '_nodrm' + '.azw4')
else:
outfile = os.path.join(outdir, outfilename + '_nodrm' + '.mobi')
mb.getMobiFile(outfile) mb.getMobiFile(outfile)
return 0 return 0
@ -158,7 +161,6 @@ def main(argv=sys.argv):
print ('K4MobiDeDrm v%(__version__)s ' print ('K4MobiDeDrm v%(__version__)s '
'provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc .' % globals()) 'provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc .' % globals())
print ' '
try: try:
opts, args = getopt.getopt(sys.argv[1:], "k:p:s:") opts, args = getopt.getopt(sys.argv[1:], "k:p:s:")
except getopt.GetoptError, err: except getopt.GetoptError, err:

@ -53,8 +53,9 @@
# files, but they are not for HUFF/CDIC compress files! # files, but they are not for HUFF/CDIC compress files!
# 0.30 - Modified interface slightly to work better with new calibre plugin style # 0.30 - Modified interface slightly to work better with new calibre plugin style
# 0.31 - The multibyte encrytion info is true for version 7 files too. # 0.31 - The multibyte encrytion info is true for version 7 files too.
# 0.32 - Added support for "Print Replica" Kindle ebooks
__version__ = '0.31' __version__ = '0.32'
import sys import sys
@ -163,6 +164,9 @@ class MobiBook:
return self.data_file[off:endoff] return self.data_file[off:endoff]
def __init__(self, infile): def __init__(self, infile):
print ('MobiDeDrm v%(__version__)s. '
'Copyright 2008-2011 The Dark Reverser et al.' % globals())
# initial sanity check on file # initial sanity check on file
self.data_file = file(infile, 'rb').read() self.data_file = file(infile, 'rb').read()
self.mobi_data = '' self.mobi_data = ''
@ -193,6 +197,7 @@ class MobiBook:
self.meta_array = {} self.meta_array = {}
return return
self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18]) self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18])
self.mobi_codepage, = struct.unpack('>L',self.sect[0x1c:0x20])
self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C]) self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C])
print "MOBI header version = %d, length = %d" %(self.mobi_version, self.mobi_length) print "MOBI header version = %d, length = %d" %(self.mobi_version, self.mobi_length)
self.extra_data_flags = 0 self.extra_data_flags = 0
@ -230,8 +235,13 @@ class MobiBook:
except: except:
self.meta_array = {} self.meta_array = {}
pass pass
self.print_replica = False
def getBookTitle(self): def getBookTitle(self):
codec_map = {
1252 : 'windows-1252',
65001 : 'utf-8',
}
title = '' title = ''
if 503 in self.meta_array: if 503 in self.meta_array:
title = self.meta_array[503] title = self.meta_array[503]
@ -242,7 +252,10 @@ class MobiBook:
if title == '': if title == '':
title = self.header[:32] title = self.header[:32]
title = title.split("\0")[0] title = title.split("\0")[0]
return title codec = 'windows-1252'
if self.mobi_codepage in codec_map.keys():
codec = codec_map[self.mobi_codepage]
return unicode(title, codec).encode('utf-8')
def getPIDMetaInfo(self): def getPIDMetaInfo(self):
rec209 = '' rec209 = ''
@ -306,6 +319,9 @@ class MobiBook:
def getMobiFile(self, outpath): def getMobiFile(self, outpath):
file(outpath,'wb').write(self.mobi_data) file(outpath,'wb').write(self.mobi_data)
def getPrintReplica(self):
return self.print_replica
def processBook(self, pidlist): def processBook(self, pidlist):
crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2]) crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2])
@ -313,10 +329,17 @@ class MobiBook:
self.crypto_type = crypto_type self.crypto_type = crypto_type
if crypto_type == 0: if crypto_type == 0:
print "This book is not encrypted." print "This book is not encrypted."
# we must still check for Print Replica
self.print_replica = (self.loadSection(1)[0:4] == '%MOP')
self.mobi_data = self.data_file self.mobi_data = self.data_file
return return
if crypto_type != 2 and crypto_type != 1: if crypto_type != 2 and crypto_type != 1:
raise DrmException("Cannot decode unknown Mobipocket encryption type %d" % crypto_type) raise DrmException("Cannot decode unknown Mobipocket encryption type %d" % crypto_type)
if 406 in self.meta_array:
data406 = self.meta_array[406]
val406, = struct.unpack('>Q',data406)
if val406 != 0:
raise DrmException("Cannot decode library or rented ebooks.")
goodpids = [] goodpids = []
for pid in pidlist: for pid in pidlist:
@ -367,7 +390,10 @@ class MobiBook:
if i%100 == 0: if i%100 == 0:
print ".", print ".",
# print "record %d, extra_size %d" %(i,extra_size) # print "record %d, extra_size %d" %(i,extra_size)
self.mobi_data += PC1(found_key, data[0:len(data) - extra_size]) decoded_data = PC1(found_key, data[0:len(data) - extra_size])
if i==1:
self.print_replica = (decoded_data[0:4] == '%MOP')
self.mobi_data += decoded_data
if extra_size > 0: if extra_size > 0:
self.mobi_data += data[-extra_size:] self.mobi_data += data[-extra_size:]
if self.num_sections > self.records+1: if self.num_sections > self.records+1:
@ -392,9 +418,9 @@ def getUnencryptedBookWithList(infile,pidlist):
def main(argv=sys.argv): def main(argv=sys.argv):
print ('MobiDeDrm v%(__version__)s. ' print ('MobiDeDrm v%(__version__)s. '
'Copyright 2008-2010 The Dark Reverser.' % globals()) 'Copyright 2008-2011 The Dark Reverser et al.' % globals())
if len(argv)<3 or len(argv)>4: if len(argv)<3 or len(argv)>4:
print "Removes protection from Mobipocket books" print "Removes protection from Kindle/Mobipocket and Kindle/Print Replica ebooks"
print "Usage:" print "Usage:"
print " %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0] print " %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0]
return 1 return 1
@ -402,9 +428,9 @@ def main(argv=sys.argv):
infile = argv[1] infile = argv[1]
outfile = argv[2] outfile = argv[2]
if len(argv) is 4: if len(argv) is 4:
pidlist = argv[3].split(',') pidlist = argv[3].split(',')
else: else:
pidlist = {} pidlist = {}
try: try:
stripped_file = getUnencryptedBookWithList(infile, pidlist) stripped_file = getUnencryptedBookWithList(infile, pidlist)
file(outfile, 'wb').write(stripped_file) file(outfile, 'wb').write(stripped_file)

@ -263,6 +263,7 @@ class PrefsDialog(Toplevel):
filetypes=[('ePub Files','.epub'), filetypes=[('ePub Files','.epub'),
('Kindle','.azw'), ('Kindle','.azw'),
('Kindle','.azw1'), ('Kindle','.azw1'),
('Kindle','.azw4'),
('Kindle','.tpz'), ('Kindle','.tpz'),
('Kindle','.mobi'), ('Kindle','.mobi'),
('Kindle','.prc'), ('Kindle','.prc'),
@ -465,7 +466,7 @@ class ConvDialog(Toplevel):
if ext == '.pdb': if ext == '.pdb':
self.p2 = processPDB(apphome, infile, outdir, rscpath) self.p2 = processPDB(apphome, infile, outdir, rscpath)
return 0 return 0
if ext in ['.azw', '.azw1', '.prc', '.mobi', '.tpz']: if ext in ['.azw', '.azw1', '.azw4', '.prc', '.mobi', '.tpz']:
self.p2 = processK4MOBI(apphome, infile, outdir, rscpath) self.p2 = processK4MOBI(apphome, infile, outdir, rscpath)
return 0 return 0
if ext == '.pdf': if ext == '.pdf':

@ -17,7 +17,7 @@ from __future__ import with_statement
# and many many others # and many many others
__version__ = '3.6' __version__ = '3.7'
class Unbuffered: class Unbuffered:
def __init__(self, stream): def __init__(self, stream):
@ -76,7 +76,7 @@ def cleanup_name(name):
def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids): def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
# handle the obvious cases at the beginning # handle the obvious cases at the beginning
if not os.path.isfile(infile): if not os.path.isfile(infile):
print "Error: Input file does not exist" print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: Input file does not exist"
return 1 return 1
mobi = True mobi = True
@ -106,17 +106,20 @@ def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
mb.processBook(pidlst) mb.processBook(pidlst)
except mobidedrm.DrmException, e: except mobidedrm.DrmException, e:
print "Error: " + str(e) + "\nDRM Removal Failed.\n" print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n"
return 1 return 1
except topazextract.TpzDRMError, e: except topazextract.TpzDRMError, e:
print "Error: " + str(e) + "\nDRM Removal Failed.\n" print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n"
return 1 return 1
except Exception, e: except Exception, e:
print "Error: " + str(e) + "\nDRM Removal Failed.\n" print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n"
return 1 return 1
if mobi: if mobi:
outfile = os.path.join(outdir, outfilename + '_nodrm' + '.mobi') if mb.getPrintReplica():
outfile = os.path.join(outdir, outfilename + '_nodrm' + '.azw4')
else:
outfile = os.path.join(outdir, outfilename + '_nodrm' + '.mobi')
mb.getMobiFile(outfile) mb.getMobiFile(outfile)
return 0 return 0
@ -158,7 +161,6 @@ def main(argv=sys.argv):
print ('K4MobiDeDrm v%(__version__)s ' print ('K4MobiDeDrm v%(__version__)s '
'provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc .' % globals()) 'provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc .' % globals())
print ' '
try: try:
opts, args = getopt.getopt(sys.argv[1:], "k:p:s:") opts, args = getopt.getopt(sys.argv[1:], "k:p:s:")
except getopt.GetoptError, err: except getopt.GetoptError, err:

@ -53,8 +53,9 @@
# files, but they are not for HUFF/CDIC compress files! # files, but they are not for HUFF/CDIC compress files!
# 0.30 - Modified interface slightly to work better with new calibre plugin style # 0.30 - Modified interface slightly to work better with new calibre plugin style
# 0.31 - The multibyte encrytion info is true for version 7 files too. # 0.31 - The multibyte encrytion info is true for version 7 files too.
# 0.32 - Added support for "Print Replica" Kindle ebooks
__version__ = '0.31' __version__ = '0.32'
import sys import sys
@ -163,6 +164,9 @@ class MobiBook:
return self.data_file[off:endoff] return self.data_file[off:endoff]
def __init__(self, infile): def __init__(self, infile):
print ('MobiDeDrm v%(__version__)s. '
'Copyright 2008-2011 The Dark Reverser et al.' % globals())
# initial sanity check on file # initial sanity check on file
self.data_file = file(infile, 'rb').read() self.data_file = file(infile, 'rb').read()
self.mobi_data = '' self.mobi_data = ''
@ -193,6 +197,7 @@ class MobiBook:
self.meta_array = {} self.meta_array = {}
return return
self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18]) self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18])
self.mobi_codepage, = struct.unpack('>L',self.sect[0x1c:0x20])
self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C]) self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C])
print "MOBI header version = %d, length = %d" %(self.mobi_version, self.mobi_length) print "MOBI header version = %d, length = %d" %(self.mobi_version, self.mobi_length)
self.extra_data_flags = 0 self.extra_data_flags = 0
@ -230,8 +235,13 @@ class MobiBook:
except: except:
self.meta_array = {} self.meta_array = {}
pass pass
self.print_replica = False
def getBookTitle(self): def getBookTitle(self):
codec_map = {
1252 : 'windows-1252',
65001 : 'utf-8',
}
title = '' title = ''
if 503 in self.meta_array: if 503 in self.meta_array:
title = self.meta_array[503] title = self.meta_array[503]
@ -242,7 +252,10 @@ class MobiBook:
if title == '': if title == '':
title = self.header[:32] title = self.header[:32]
title = title.split("\0")[0] title = title.split("\0")[0]
return title codec = 'windows-1252'
if self.mobi_codepage in codec_map.keys():
codec = codec_map[self.mobi_codepage]
return unicode(title, codec).encode('utf-8')
def getPIDMetaInfo(self): def getPIDMetaInfo(self):
rec209 = '' rec209 = ''
@ -306,6 +319,9 @@ class MobiBook:
def getMobiFile(self, outpath): def getMobiFile(self, outpath):
file(outpath,'wb').write(self.mobi_data) file(outpath,'wb').write(self.mobi_data)
def getPrintReplica(self):
return self.print_replica
def processBook(self, pidlist): def processBook(self, pidlist):
crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2]) crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2])
@ -313,10 +329,17 @@ class MobiBook:
self.crypto_type = crypto_type self.crypto_type = crypto_type
if crypto_type == 0: if crypto_type == 0:
print "This book is not encrypted." print "This book is not encrypted."
# we must still check for Print Replica
self.print_replica = (self.loadSection(1)[0:4] == '%MOP')
self.mobi_data = self.data_file self.mobi_data = self.data_file
return return
if crypto_type != 2 and crypto_type != 1: if crypto_type != 2 and crypto_type != 1:
raise DrmException("Cannot decode unknown Mobipocket encryption type %d" % crypto_type) raise DrmException("Cannot decode unknown Mobipocket encryption type %d" % crypto_type)
if 406 in self.meta_array:
data406 = self.meta_array[406]
val406, = struct.unpack('>Q',data406)
if val406 != 0:
raise DrmException("Cannot decode library or rented ebooks.")
goodpids = [] goodpids = []
for pid in pidlist: for pid in pidlist:
@ -367,7 +390,10 @@ class MobiBook:
if i%100 == 0: if i%100 == 0:
print ".", print ".",
# print "record %d, extra_size %d" %(i,extra_size) # print "record %d, extra_size %d" %(i,extra_size)
self.mobi_data += PC1(found_key, data[0:len(data) - extra_size]) decoded_data = PC1(found_key, data[0:len(data) - extra_size])
if i==1:
self.print_replica = (decoded_data[0:4] == '%MOP')
self.mobi_data += decoded_data
if extra_size > 0: if extra_size > 0:
self.mobi_data += data[-extra_size:] self.mobi_data += data[-extra_size:]
if self.num_sections > self.records+1: if self.num_sections > self.records+1:
@ -392,9 +418,9 @@ def getUnencryptedBookWithList(infile,pidlist):
def main(argv=sys.argv): def main(argv=sys.argv):
print ('MobiDeDrm v%(__version__)s. ' print ('MobiDeDrm v%(__version__)s. '
'Copyright 2008-2010 The Dark Reverser.' % globals()) 'Copyright 2008-2011 The Dark Reverser et al.' % globals())
if len(argv)<3 or len(argv)>4: if len(argv)<3 or len(argv)>4:
print "Removes protection from Mobipocket books" print "Removes protection from Kindle/Mobipocket and Kindle/Print Replica ebooks"
print "Usage:" print "Usage:"
print " %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0] print " %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0]
return 1 return 1
@ -402,9 +428,9 @@ def main(argv=sys.argv):
infile = argv[1] infile = argv[1]
outfile = argv[2] outfile = argv[2]
if len(argv) is 4: if len(argv) is 4:
pidlist = argv[3].split(',') pidlist = argv[3].split(',')
else: else:
pidlist = {} pidlist = {}
try: try:
stripped_file = getUnencryptedBookWithList(infile, pidlist) stripped_file = getUnencryptedBookWithList(infile, pidlist)
file(outfile, 'wb').write(stripped_file) file(outfile, 'wb').write(stripped_file)

@ -109,7 +109,7 @@ class MainDialog(Tkinter.Frame):
# post output from subprocess in scrolled text widget # post output from subprocess in scrolled text widget
def showCmdOutput(self, msg): def showCmdOutput(self, msg):
if msg and msg !='': if msg and msg !='':
msg = msg.encode('utf-8') # msg = msg.encode('utf-8')
if sys.platform.startswith('win'): if sys.platform.startswith('win'):
msg = msg.replace('\r\n','\n') msg = msg.replace('\r\n','\n')
self.stext.insert(Tkconstants.END,msg) self.stext.insert(Tkconstants.END,msg)
@ -149,7 +149,7 @@ class MainDialog(Tkinter.Frame):
mobipath = tkFileDialog.askopenfilename( mobipath = tkFileDialog.askopenfilename(
initialdir = cpath, initialdir = cpath,
parent=None, title='Select Kindle/Mobi/Topaz eBook File', parent=None, title='Select Kindle/Mobi/Topaz eBook File',
defaultextension='.prc', filetypes=[('Mobi eBook File', '.prc'), ('Mobi eBook File', '.azw'),('Mobi eBook File', '.mobi'),('Mobi eBook File', '.tpz'),('Mobi eBook File', '.azw1'),('All Files', '.*')]) defaultextension='.prc', filetypes=[('Mobi eBook File', '.prc'), ('Mobi eBook File', '.azw'),('Mobi eBook File', '.mobi'),('Mobi eBook File', '.tpz'),('Mobi eBook File', '.azw1'),('Mobi azw4 eBook File', '.azw4'),('All Files', '.*')])
if mobipath: if mobipath:
mobipath = os.path.normpath(mobipath) mobipath = os.path.normpath(mobipath)
self.mobipath.delete(0, Tkconstants.END) self.mobipath.delete(0, Tkconstants.END)

@ -17,7 +17,7 @@ from __future__ import with_statement
# and many many others # and many many others
__version__ = '3.6' __version__ = '3.7'
class Unbuffered: class Unbuffered:
def __init__(self, stream): def __init__(self, stream):
@ -76,7 +76,7 @@ def cleanup_name(name):
def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids): def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
# handle the obvious cases at the beginning # handle the obvious cases at the beginning
if not os.path.isfile(infile): if not os.path.isfile(infile):
print "Error: Input file does not exist" print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: Input file does not exist"
return 1 return 1
mobi = True mobi = True
@ -106,17 +106,20 @@ def decryptBook(infile, outdir, k4, kInfoFiles, serials, pids):
mb.processBook(pidlst) mb.processBook(pidlst)
except mobidedrm.DrmException, e: except mobidedrm.DrmException, e:
print "Error: " + str(e) + "\nDRM Removal Failed.\n" print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n"
return 1 return 1
except topazextract.TpzDRMError, e: except topazextract.TpzDRMError, e:
print "Error: " + str(e) + "\nDRM Removal Failed.\n" print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n"
return 1 return 1
except Exception, e: except Exception, e:
print "Error: " + str(e) + "\nDRM Removal Failed.\n" print >>sys.stderr, ('K4MobiDeDrm v%(__version__)s\n' % globals()) + "Error: " + str(e) + "\nDRM Removal Failed.\n"
return 1 return 1
if mobi: if mobi:
outfile = os.path.join(outdir, outfilename + '_nodrm' + '.mobi') if mb.getPrintReplica():
outfile = os.path.join(outdir, outfilename + '_nodrm' + '.azw4')
else:
outfile = os.path.join(outdir, outfilename + '_nodrm' + '.mobi')
mb.getMobiFile(outfile) mb.getMobiFile(outfile)
return 0 return 0
@ -158,7 +161,6 @@ def main(argv=sys.argv):
print ('K4MobiDeDrm v%(__version__)s ' print ('K4MobiDeDrm v%(__version__)s '
'provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc .' % globals()) 'provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc .' % globals())
print ' '
try: try:
opts, args = getopt.getopt(sys.argv[1:], "k:p:s:") opts, args = getopt.getopt(sys.argv[1:], "k:p:s:")
except getopt.GetoptError, err: except getopt.GetoptError, err:

@ -53,8 +53,9 @@
# files, but they are not for HUFF/CDIC compress files! # files, but they are not for HUFF/CDIC compress files!
# 0.30 - Modified interface slightly to work better with new calibre plugin style # 0.30 - Modified interface slightly to work better with new calibre plugin style
# 0.31 - The multibyte encrytion info is true for version 7 files too. # 0.31 - The multibyte encrytion info is true for version 7 files too.
# 0.32 - Added support for "Print Replica" Kindle ebooks
__version__ = '0.31' __version__ = '0.32'
import sys import sys
@ -163,6 +164,9 @@ class MobiBook:
return self.data_file[off:endoff] return self.data_file[off:endoff]
def __init__(self, infile): def __init__(self, infile):
print ('MobiDeDrm v%(__version__)s. '
'Copyright 2008-2011 The Dark Reverser et al.' % globals())
# initial sanity check on file # initial sanity check on file
self.data_file = file(infile, 'rb').read() self.data_file = file(infile, 'rb').read()
self.mobi_data = '' self.mobi_data = ''
@ -193,6 +197,7 @@ class MobiBook:
self.meta_array = {} self.meta_array = {}
return return
self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18]) self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18])
self.mobi_codepage, = struct.unpack('>L',self.sect[0x1c:0x20])
self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C]) self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C])
print "MOBI header version = %d, length = %d" %(self.mobi_version, self.mobi_length) print "MOBI header version = %d, length = %d" %(self.mobi_version, self.mobi_length)
self.extra_data_flags = 0 self.extra_data_flags = 0
@ -230,8 +235,13 @@ class MobiBook:
except: except:
self.meta_array = {} self.meta_array = {}
pass pass
self.print_replica = False
def getBookTitle(self): def getBookTitle(self):
codec_map = {
1252 : 'windows-1252',
65001 : 'utf-8',
}
title = '' title = ''
if 503 in self.meta_array: if 503 in self.meta_array:
title = self.meta_array[503] title = self.meta_array[503]
@ -242,7 +252,10 @@ class MobiBook:
if title == '': if title == '':
title = self.header[:32] title = self.header[:32]
title = title.split("\0")[0] title = title.split("\0")[0]
return title codec = 'windows-1252'
if self.mobi_codepage in codec_map.keys():
codec = codec_map[self.mobi_codepage]
return unicode(title, codec).encode('utf-8')
def getPIDMetaInfo(self): def getPIDMetaInfo(self):
rec209 = '' rec209 = ''
@ -306,6 +319,9 @@ class MobiBook:
def getMobiFile(self, outpath): def getMobiFile(self, outpath):
file(outpath,'wb').write(self.mobi_data) file(outpath,'wb').write(self.mobi_data)
def getPrintReplica(self):
return self.print_replica
def processBook(self, pidlist): def processBook(self, pidlist):
crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2]) crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2])
@ -313,10 +329,17 @@ class MobiBook:
self.crypto_type = crypto_type self.crypto_type = crypto_type
if crypto_type == 0: if crypto_type == 0:
print "This book is not encrypted." print "This book is not encrypted."
# we must still check for Print Replica
self.print_replica = (self.loadSection(1)[0:4] == '%MOP')
self.mobi_data = self.data_file self.mobi_data = self.data_file
return return
if crypto_type != 2 and crypto_type != 1: if crypto_type != 2 and crypto_type != 1:
raise DrmException("Cannot decode unknown Mobipocket encryption type %d" % crypto_type) raise DrmException("Cannot decode unknown Mobipocket encryption type %d" % crypto_type)
if 406 in self.meta_array:
data406 = self.meta_array[406]
val406, = struct.unpack('>Q',data406)
if val406 != 0:
raise DrmException("Cannot decode library or rented ebooks.")
goodpids = [] goodpids = []
for pid in pidlist: for pid in pidlist:
@ -367,7 +390,10 @@ class MobiBook:
if i%100 == 0: if i%100 == 0:
print ".", print ".",
# print "record %d, extra_size %d" %(i,extra_size) # print "record %d, extra_size %d" %(i,extra_size)
self.mobi_data += PC1(found_key, data[0:len(data) - extra_size]) decoded_data = PC1(found_key, data[0:len(data) - extra_size])
if i==1:
self.print_replica = (decoded_data[0:4] == '%MOP')
self.mobi_data += decoded_data
if extra_size > 0: if extra_size > 0:
self.mobi_data += data[-extra_size:] self.mobi_data += data[-extra_size:]
if self.num_sections > self.records+1: if self.num_sections > self.records+1:
@ -392,9 +418,9 @@ def getUnencryptedBookWithList(infile,pidlist):
def main(argv=sys.argv): def main(argv=sys.argv):
print ('MobiDeDrm v%(__version__)s. ' print ('MobiDeDrm v%(__version__)s. '
'Copyright 2008-2010 The Dark Reverser.' % globals()) 'Copyright 2008-2011 The Dark Reverser et al.' % globals())
if len(argv)<3 or len(argv)>4: if len(argv)<3 or len(argv)>4:
print "Removes protection from Mobipocket books" print "Removes protection from Kindle/Mobipocket and Kindle/Print Replica ebooks"
print "Usage:" print "Usage:"
print " %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0] print " %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0]
return 1 return 1
@ -402,9 +428,9 @@ def main(argv=sys.argv):
infile = argv[1] infile = argv[1]
outfile = argv[2] outfile = argv[2]
if len(argv) is 4: if len(argv) is 4:
pidlist = argv[3].split(',') pidlist = argv[3].split(',')
else: else:
pidlist = {} pidlist = {}
try: try:
stripped_file = getUnencryptedBookWithList(infile, pidlist) stripped_file = getUnencryptedBookWithList(infile, pidlist)
file(outfile, 'wb').write(stripped_file) file(outfile, 'wb').write(stripped_file)

@ -53,8 +53,9 @@
# files, but they are not for HUFF/CDIC compress files! # files, but they are not for HUFF/CDIC compress files!
# 0.30 - Modified interface slightly to work better with new calibre plugin style # 0.30 - Modified interface slightly to work better with new calibre plugin style
# 0.31 - The multibyte encrytion info is true for version 7 files too. # 0.31 - The multibyte encrytion info is true for version 7 files too.
# 0.32 - Added support for "Print Replica" Kindle ebooks
__version__ = '0.31' __version__ = '0.32'
import sys import sys
@ -163,6 +164,9 @@ class MobiBook:
return self.data_file[off:endoff] return self.data_file[off:endoff]
def __init__(self, infile): def __init__(self, infile):
print ('MobiDeDrm v%(__version__)s. '
'Copyright 2008-2011 The Dark Reverser et al.' % globals())
# initial sanity check on file # initial sanity check on file
self.data_file = file(infile, 'rb').read() self.data_file = file(infile, 'rb').read()
self.mobi_data = '' self.mobi_data = ''
@ -193,6 +197,7 @@ class MobiBook:
self.meta_array = {} self.meta_array = {}
return return
self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18]) self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18])
self.mobi_codepage, = struct.unpack('>L',self.sect[0x1c:0x20])
self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C]) self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C])
print "MOBI header version = %d, length = %d" %(self.mobi_version, self.mobi_length) print "MOBI header version = %d, length = %d" %(self.mobi_version, self.mobi_length)
self.extra_data_flags = 0 self.extra_data_flags = 0
@ -230,8 +235,13 @@ class MobiBook:
except: except:
self.meta_array = {} self.meta_array = {}
pass pass
self.print_replica = False
def getBookTitle(self): def getBookTitle(self):
codec_map = {
1252 : 'windows-1252',
65001 : 'utf-8',
}
title = '' title = ''
if 503 in self.meta_array: if 503 in self.meta_array:
title = self.meta_array[503] title = self.meta_array[503]
@ -242,7 +252,10 @@ class MobiBook:
if title == '': if title == '':
title = self.header[:32] title = self.header[:32]
title = title.split("\0")[0] title = title.split("\0")[0]
return title codec = 'windows-1252'
if self.mobi_codepage in codec_map.keys():
codec = codec_map[self.mobi_codepage]
return unicode(title, codec).encode('utf-8')
def getPIDMetaInfo(self): def getPIDMetaInfo(self):
rec209 = '' rec209 = ''
@ -306,6 +319,9 @@ class MobiBook:
def getMobiFile(self, outpath): def getMobiFile(self, outpath):
file(outpath,'wb').write(self.mobi_data) file(outpath,'wb').write(self.mobi_data)
def getPrintReplica(self):
return self.print_replica
def processBook(self, pidlist): def processBook(self, pidlist):
crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2]) crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2])
@ -313,10 +329,17 @@ class MobiBook:
self.crypto_type = crypto_type self.crypto_type = crypto_type
if crypto_type == 0: if crypto_type == 0:
print "This book is not encrypted." print "This book is not encrypted."
# we must still check for Print Replica
self.print_replica = (self.loadSection(1)[0:4] == '%MOP')
self.mobi_data = self.data_file self.mobi_data = self.data_file
return return
if crypto_type != 2 and crypto_type != 1: if crypto_type != 2 and crypto_type != 1:
raise DrmException("Cannot decode unknown Mobipocket encryption type %d" % crypto_type) raise DrmException("Cannot decode unknown Mobipocket encryption type %d" % crypto_type)
if 406 in self.meta_array:
data406 = self.meta_array[406]
val406, = struct.unpack('>Q',data406)
if val406 != 0:
raise DrmException("Cannot decode library or rented ebooks.")
goodpids = [] goodpids = []
for pid in pidlist: for pid in pidlist:
@ -367,7 +390,10 @@ class MobiBook:
if i%100 == 0: if i%100 == 0:
print ".", print ".",
# print "record %d, extra_size %d" %(i,extra_size) # print "record %d, extra_size %d" %(i,extra_size)
self.mobi_data += PC1(found_key, data[0:len(data) - extra_size]) decoded_data = PC1(found_key, data[0:len(data) - extra_size])
if i==1:
self.print_replica = (decoded_data[0:4] == '%MOP')
self.mobi_data += decoded_data
if extra_size > 0: if extra_size > 0:
self.mobi_data += data[-extra_size:] self.mobi_data += data[-extra_size:]
if self.num_sections > self.records+1: if self.num_sections > self.records+1:
@ -392,9 +418,9 @@ def getUnencryptedBookWithList(infile,pidlist):
def main(argv=sys.argv): def main(argv=sys.argv):
print ('MobiDeDrm v%(__version__)s. ' print ('MobiDeDrm v%(__version__)s. '
'Copyright 2008-2010 The Dark Reverser.' % globals()) 'Copyright 2008-2011 The Dark Reverser et al.' % globals())
if len(argv)<3 or len(argv)>4: if len(argv)<3 or len(argv)>4:
print "Removes protection from Mobipocket books" print "Removes protection from Kindle/Mobipocket and Kindle/Print Replica ebooks"
print "Usage:" print "Usage:"
print " %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0] print " %s <infile> <outfile> [<Comma separated list of PIDs to try>]" % sys.argv[0]
return 1 return 1
@ -402,9 +428,9 @@ def main(argv=sys.argv):
infile = argv[1] infile = argv[1]
outfile = argv[2] outfile = argv[2]
if len(argv) is 4: if len(argv) is 4:
pidlist = argv[3].split(',') pidlist = argv[3].split(',')
else: else:
pidlist = {} pidlist = {}
try: try:
stripped_file = getUnencryptedBookWithList(infile, pidlist) stripped_file = getUnencryptedBookWithList(infile, pidlist)
file(outfile, 'wb').write(stripped_file) file(outfile, 'wb').write(stripped_file)

Loading…
Cancel
Save