From 4163d5ccf4af927d9aac1cdb670303ec852f5d61 Mon Sep 17 00:00:00 2001 From: Apprentice Alf Date: Mon, 28 Mar 2011 13:01:05 +0100 Subject: [PATCH] tools v3.8 --- Calibre_Plugins/K4MobiDeDRM_plugin/genbook.py | 25 +++++--- .../K4MobiDeDRM_plugin/kgenpids.py | 44 ++++++-------- Calibre_Plugins/k4mobidedrm_plugin.zip | Bin 44219 -> 44385 bytes .../k4mobidedrm_plugin/k4mobidedrm_plugin.py | 4 +- .../k4mobidedrm_plugin/k4mutils.py | 54 ++++++++++-------- .../k4mobidedrm_plugin/k4pcutils.py | 35 +++++++----- .../DeDRM.app/Contents/Info.plist | 4 +- .../DeDRM.app/Contents/Resources/genbook.py | 25 +++++--- .../Contents/Resources/k4mobidedrm.py | 4 +- .../DeDRM.app/Contents/Resources/k4mutils.py | 54 ++++++++++-------- .../DeDRM.app/Contents/Resources/k4pcutils.py | 35 +++++++----- .../DeDRM.app/Contents/Resources/kgenpids.py | 44 ++++++-------- .../DeDRM_WinApp/DeDRM_lib/lib/genbook.py | 25 +++++--- .../DeDRM_WinApp/DeDRM_lib/lib/k4mobidedrm.py | 4 +- .../DeDRM_WinApp/DeDRM_lib/lib/k4mutils.py | 54 ++++++++++-------- .../DeDRM_WinApp/DeDRM_lib/lib/k4pcutils.py | 35 +++++++----- .../DeDRM_WinApp/DeDRM_lib/lib/kgenpids.py | 44 ++++++-------- KindleBooks_Tools/KindleBooks/lib/genbook.py | 25 +++++--- .../KindleBooks/lib/k4mobidedrm.py | 4 +- KindleBooks_Tools/KindleBooks/lib/k4mutils.py | 54 ++++++++++-------- .../KindleBooks/lib/k4pcutils.py | 35 +++++++----- KindleBooks_Tools/KindleBooks/lib/kgenpids.py | 44 ++++++-------- 22 files changed, 346 insertions(+), 306 deletions(-) diff --git a/Calibre_Plugins/K4MobiDeDRM_plugin/genbook.py b/Calibre_Plugins/K4MobiDeDRM_plugin/genbook.py index 19be51a..11258d1 100644 --- a/Calibre_Plugins/K4MobiDeDRM_plugin/genbook.py +++ b/Calibre_Plugins/K4MobiDeDRM_plugin/genbook.py @@ -323,12 +323,12 @@ def generateBook(bookDir, raw, fixedimage): meta_array = getMetaArray(metaFile) # replace special chars in title and authors like & < > - title = meta_array['Title'] + title = meta_array.get('Title','No Title Provided') title = title.replace('&','&') title = title.replace('<','<') title = title.replace('>','>') meta_array['Title'] = title - authors = meta_array['Authors'] + authors = meta_array.get('Authors','No Authors Provided') authors = authors.replace('&','&') authors = authors.replace('<','<') authors = authors.replace('>','>') @@ -413,8 +413,10 @@ def generateBook(bookDir, raw, fixedimage): htmlstr += '' + meta_array['Title'] + ' by ' + meta_array['Authors'] + '\n' htmlstr += '\n' htmlstr += '\n' - htmlstr += '\n' - htmlstr += '\n' + if 'ASIN' in meta_array: + htmlstr += '\n' + if 'GUID' in meta_array: + htmlstr += '\n' htmlstr += '\n' htmlstr += '\n\n' @@ -430,8 +432,10 @@ def generateBook(bookDir, raw, fixedimage): svgindex += '' + meta_array['Title'] + '\n' svgindex += '\n' svgindex += '\n' - svgindex += '\n' - svgindex += '\n' + if 'ASIN' in meta_array: + svgindex += '\n' + if 'GUID' in meta_array: + svgindex += '\n' svgindex += '\n' svgindex += '\n' @@ -485,9 +489,12 @@ def generateBook(bookDir, raw, fixedimage): opfstr += '\n' # adding metadata opfstr += ' \n' - opfstr += ' ' + meta_array['GUID'] + '\n' - opfstr += ' ' + meta_array['ASIN'] + '\n' - opfstr += ' ' + meta_array['oASIN'] + '\n' + if 'GUID' in meta_array: + opfstr += ' ' + meta_array['GUID'] + '\n' + if 'ASIN' in meta_array: + opfstr += ' ' + meta_array['ASIN'] + '\n' + if 'oASIN' in meta_array: + opfstr += ' ' + meta_array['oASIN'] + '\n' opfstr += ' ' + meta_array['Title'] + '\n' opfstr += ' ' + meta_array['Authors'] + '\n' opfstr += ' en\n' diff --git a/Calibre_Plugins/K4MobiDeDRM_plugin/kgenpids.py b/Calibre_Plugins/K4MobiDeDRM_plugin/kgenpids.py index 039daf9..c10f105 100644 --- a/Calibre_Plugins/K4MobiDeDRM_plugin/kgenpids.py +++ b/Calibre_Plugins/K4MobiDeDRM_plugin/kgenpids.py @@ -18,9 +18,9 @@ global charMap3 global charMap4 if sys.platform.startswith('win'): - from k4pcutils import openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap2 + from k4pcutils import getKindleInfoFiles, parseKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap2 if sys.platform.startswith('darwin'): - from k4mutils import openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap2 + from k4mutils import getKindleInfoFiles, parseKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap2 charMap1 = "n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M" charMap3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" @@ -67,22 +67,6 @@ def decode(data,map): result += pack("B",value) return result - -# Parse the Kindle.info file and return the records as a list of key-values -def parseKindleInfo(kInfoFile): - DB = {} - infoReader = openKindleInfo(kInfoFile) - infoReader.read(1) - data = infoReader.read() - if sys.platform.startswith('win'): - items = data.split('{') - else : - items = data.split('[') - for item in items: - splito = item.split(':') - DB[splito[0]] =splito[1] - return DB - # Get a record from the Kindle.info file for the key "hashedKey" (already hashed and encoded). # Return the decoded and decrypted record def getKindleInfoValueForHash(hashedKey): @@ -241,7 +225,7 @@ def getKindlePid(pidlst, rec209, token, serialnum): # Parse the EXTH header records and parse the Kindleinfo # file to calculate the book pid. -def getK4Pids(pidlst, rec209, token, kInfoFile=None): +def getK4Pids(pidlst, rec209, token, kInfoFile): global kindleDatabase global charMap1 kindleDatabase = None @@ -254,10 +238,17 @@ def getK4Pids(pidlst, rec209, token, kInfoFile=None): if kindleDatabase == None : return pidlst + + try: + # Get the Mazama Random number + MazamaRandomNumber = getKindleInfoValueForKey("MazamaRandomNumber") - # Get the Mazama Random number - MazamaRandomNumber = getKindleInfoValueForKey("MazamaRandomNumber") - + # Get the kindle account token + kindleAccountToken = getKindleInfoValueForKey("kindle.account.tokens") + except KeyError: + print "Keys not found in " + kInfoFile + return pidlst + # Get the HDD serial encodedSystemVolumeSerialNumber = encodeHash(GetVolumeSerialNumber(),charMap1) @@ -273,10 +264,7 @@ def getK4Pids(pidlst, rec209, token, kInfoFile=None): devicePID = checksumPid(devicePID) pidlst.append(devicePID) - # Compute book PID - - # Get the kindle account token - kindleAccountToken = getKindleInfoValueForKey("kindle.account.tokens") + # Compute book PIDs # book pid pidHash = SHA1(DSN+kindleAccountToken+rec209+token) @@ -300,8 +288,10 @@ def getK4Pids(pidlst, rec209, token, kInfoFile=None): def getPidList(md1, md2, k4, pids, serials, kInfoFiles): pidlst = [] + if kInfoFiles is None: + kInfoFiles = [] if k4: - pidlst = getK4Pids(pidlst, md1, md2) + kInfoFiles = getKindleInfoFiles(kInfoFiles) for infoFile in kInfoFiles: pidlst = getK4Pids(pidlst, md1, md2, infoFile) for serialnum in serials: diff --git a/Calibre_Plugins/k4mobidedrm_plugin.zip b/Calibre_Plugins/k4mobidedrm_plugin.zip index 6b497899270362f79e5b94f1a10a42394d0cc667..83afae4f5764668b5b47961cf68b99f6298e02e7 100644 GIT binary patch delta 16951 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 UnKdCyWiux$t+iz1T?z6e0DQ3IQvd(} diff --git a/Calibre_Plugins/k4mobidedrm_plugin/k4mobidedrm_plugin.py b/Calibre_Plugins/k4mobidedrm_plugin/k4mobidedrm_plugin.py index d165f37..3ae1afa 100644 --- a/Calibre_Plugins/k4mobidedrm_plugin/k4mobidedrm_plugin.py +++ b/Calibre_Plugins/k4mobidedrm_plugin/k4mobidedrm_plugin.py @@ -29,7 +29,7 @@ from __future__ import with_statement # and import that ZIP into Calibre using its plugin configuration GUI. -__version__ = '2.7' +__version__ = '2.8' class Unbuffered: def __init__(self, stream): @@ -250,7 +250,7 @@ if not __name__ == "__main__" and inCalibre: Provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc.' supported_platforms = ['osx', 'windows', 'linux'] # Platforms this plugin will run on author = 'DiapDealer, SomeUpdates' # The author of this plugin - version = (0, 2, 7) # The version number of this plugin + version = (0, 2, 8) # The version number of this plugin file_types = set(['prc','mobi','azw','azw1','tpz']) # The file types that this plugin will be applied to on_import = True # Run this plugin during the import priority = 210 # run this plugin before mobidedrm, k4pcdedrm, k4dedrm diff --git a/Calibre_Plugins/k4mobidedrm_plugin/k4mutils.py b/Calibre_Plugins/k4mobidedrm_plugin/k4mutils.py index 5e57701..539723d 100644 --- a/Calibre_Plugins/k4mobidedrm_plugin/k4mutils.py +++ b/Calibre_Plugins/k4mobidedrm_plugin/k4mutils.py @@ -168,27 +168,33 @@ def CryptUnprotectData(encryptedData): return cleartext -# Locate and open the .kindle-info file -def openKindleInfo(kInfoFile=None): - if kInfoFile == None: - home = os.getenv('HOME') - cmdline = 'find "' + home + '/Library/Application Support" -name ".kindle-info"' - cmdline = cmdline.encode(sys.getfilesystemencoding()) - p1 = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False) - out1, out2 = p1.communicate() - reslst = out1.split('\n') - kinfopath = 'NONE' - cnt = len(reslst) - for j in xrange(cnt): - resline = reslst[j] - pp = resline.find('.kindle-info') - if pp >= 0: - kinfopath = resline - break - if not os.path.isfile(kinfopath): - raise DrmException('Error: .kindle-info file can not be found') - return open(kinfopath,'r') - else: - if not os.path.isfile(kInfoFile): - raise DrmException('Error: kindle-info file can not be found') - return open(kInfoFile, 'r') +# Locate the .kindle-info files +def getKindleInfoFiles(kInfoFiles): + home = os.getenv('HOME') + cmdline = 'find "' + home + '/Library/Application Support" -name ".kindle-info"' + cmdline = cmdline.encode(sys.getfilesystemencoding()) + p1 = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False) + out1, out2 = p1.communicate() + reslst = out1.split('\n') + kinfopath = 'NONE' + found = False + cnt = len(reslst) + for resline in reslst: + if os.path.isfile(resline): + kInfoFiles.append(resline) + found = True + if not found: + print('No .kindle-info files have been found.') + return kInfoFiles + +# Parse the Kindle.info file and return the records as a list of key-values +def parseKindleInfo(kInfoFile): + DB = {} + infoReader = open(kInfoFile, 'r') + infoReader.read(1) + data = infoReader.read() + items = data.split('[') + for item in items: + splito = item.split(':') + DB[splito[0]] =splito[1] + return DB diff --git a/Calibre_Plugins/k4mobidedrm_plugin/k4pcutils.py b/Calibre_Plugins/k4mobidedrm_plugin/k4pcutils.py index efc310d..d460a70 100644 --- a/Calibre_Plugins/k4mobidedrm_plugin/k4pcutils.py +++ b/Calibre_Plugins/k4mobidedrm_plugin/k4pcutils.py @@ -93,18 +93,25 @@ def CryptUnprotectData(): return CryptUnprotectData CryptUnprotectData = CryptUnprotectData() -# -# Locate and open the Kindle.info file. -# -def openKindleInfo(kInfoFile=None): - if kInfoFile == None: - regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\") - path = winreg.QueryValueEx(regkey, 'Local AppData')[0] - kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info' - if not os.path.isfile(kinfopath): - raise DrmException('Error: kindle.info file can not be found') - return open(kinfopath,'r') +# Locate the .kindle-info files +def getKindleInfoFiles(kInfoFiles): + regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\") + path = winreg.QueryValueEx(regkey, 'Local AppData')[0] + kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info' + if not os.path.isfile(kinfopath): + print('The kindle.info files has not been found.') else: - if not os.path.isfile(kInfoFile): - raise DrmException('Error: kindle.info file can not be found') - return open(kInfoFile, 'r') + kInfoFiles.append(kinfopath) + return kInfoFiles + +# Parse the Kindle.info file and return the records as a list of key-values +def parseKindleInfo(kInfoFile): + DB = {} + infoReader = open(kInfoFile, 'r') + infoReader.read(1) + data = infoReader.read() + items = data.split('{') + for item in items: + splito = item.split(':') + DB[splito[0]] =splito[1] + return DB diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Info.plist b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Info.plist index f1761ac..c99622a 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Info.plist +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Info.plist @@ -24,7 +24,7 @@ CFBundleExecutable droplet CFBundleGetInfoString - DeDRM 2.5, Written 2010–2011 by Apprentice Alf and others. + DeDRM 2.6, Written 2010–2011 by Apprentice Alf and others. CFBundleIconFile droplet CFBundleInfoDictionaryVersion @@ -34,7 +34,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 2.5 + 2.6 CFBundleSignature dplt LSMinimumSystemVersion diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/genbook.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/genbook.py index 19be51a..11258d1 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/genbook.py +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/genbook.py @@ -323,12 +323,12 @@ def generateBook(bookDir, raw, fixedimage): meta_array = getMetaArray(metaFile) # replace special chars in title and authors like & < > - title = meta_array['Title'] + title = meta_array.get('Title','No Title Provided') title = title.replace('&','&') title = title.replace('<','<') title = title.replace('>','>') meta_array['Title'] = title - authors = meta_array['Authors'] + authors = meta_array.get('Authors','No Authors Provided') authors = authors.replace('&','&') authors = authors.replace('<','<') authors = authors.replace('>','>') @@ -413,8 +413,10 @@ def generateBook(bookDir, raw, fixedimage): htmlstr += '' + meta_array['Title'] + ' by ' + meta_array['Authors'] + '\n' htmlstr += '\n' htmlstr += '\n' - htmlstr += '\n' - htmlstr += '\n' + if 'ASIN' in meta_array: + htmlstr += '\n' + if 'GUID' in meta_array: + htmlstr += '\n' htmlstr += '\n' htmlstr += '\n\n' @@ -430,8 +432,10 @@ def generateBook(bookDir, raw, fixedimage): svgindex += '' + meta_array['Title'] + '\n' svgindex += '\n' svgindex += '\n' - svgindex += '\n' - svgindex += '\n' + if 'ASIN' in meta_array: + svgindex += '\n' + if 'GUID' in meta_array: + svgindex += '\n' svgindex += '\n' svgindex += '\n' @@ -485,9 +489,12 @@ def generateBook(bookDir, raw, fixedimage): opfstr += '\n' # adding metadata opfstr += ' \n' - opfstr += ' ' + meta_array['GUID'] + '\n' - opfstr += ' ' + meta_array['ASIN'] + '\n' - opfstr += ' ' + meta_array['oASIN'] + '\n' + if 'GUID' in meta_array: + opfstr += ' ' + meta_array['GUID'] + '\n' + if 'ASIN' in meta_array: + opfstr += ' ' + meta_array['ASIN'] + '\n' + if 'oASIN' in meta_array: + opfstr += ' ' + meta_array['oASIN'] + '\n' opfstr += ' ' + meta_array['Title'] + '\n' opfstr += ' ' + meta_array['Authors'] + '\n' opfstr += ' en\n' diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4mobidedrm.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4mobidedrm.py index d165f37..3ae1afa 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4mobidedrm.py +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4mobidedrm.py @@ -29,7 +29,7 @@ from __future__ import with_statement # and import that ZIP into Calibre using its plugin configuration GUI. -__version__ = '2.7' +__version__ = '2.8' class Unbuffered: def __init__(self, stream): @@ -250,7 +250,7 @@ if not __name__ == "__main__" and inCalibre: Provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc.' supported_platforms = ['osx', 'windows', 'linux'] # Platforms this plugin will run on author = 'DiapDealer, SomeUpdates' # The author of this plugin - version = (0, 2, 7) # The version number of this plugin + version = (0, 2, 8) # The version number of this plugin file_types = set(['prc','mobi','azw','azw1','tpz']) # The file types that this plugin will be applied to on_import = True # Run this plugin during the import priority = 210 # run this plugin before mobidedrm, k4pcdedrm, k4dedrm diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4mutils.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4mutils.py index 5e57701..539723d 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4mutils.py +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4mutils.py @@ -168,27 +168,33 @@ def CryptUnprotectData(encryptedData): return cleartext -# Locate and open the .kindle-info file -def openKindleInfo(kInfoFile=None): - if kInfoFile == None: - home = os.getenv('HOME') - cmdline = 'find "' + home + '/Library/Application Support" -name ".kindle-info"' - cmdline = cmdline.encode(sys.getfilesystemencoding()) - p1 = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False) - out1, out2 = p1.communicate() - reslst = out1.split('\n') - kinfopath = 'NONE' - cnt = len(reslst) - for j in xrange(cnt): - resline = reslst[j] - pp = resline.find('.kindle-info') - if pp >= 0: - kinfopath = resline - break - if not os.path.isfile(kinfopath): - raise DrmException('Error: .kindle-info file can not be found') - return open(kinfopath,'r') - else: - if not os.path.isfile(kInfoFile): - raise DrmException('Error: kindle-info file can not be found') - return open(kInfoFile, 'r') +# Locate the .kindle-info files +def getKindleInfoFiles(kInfoFiles): + home = os.getenv('HOME') + cmdline = 'find "' + home + '/Library/Application Support" -name ".kindle-info"' + cmdline = cmdline.encode(sys.getfilesystemencoding()) + p1 = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False) + out1, out2 = p1.communicate() + reslst = out1.split('\n') + kinfopath = 'NONE' + found = False + cnt = len(reslst) + for resline in reslst: + if os.path.isfile(resline): + kInfoFiles.append(resline) + found = True + if not found: + print('No .kindle-info files have been found.') + return kInfoFiles + +# Parse the Kindle.info file and return the records as a list of key-values +def parseKindleInfo(kInfoFile): + DB = {} + infoReader = open(kInfoFile, 'r') + infoReader.read(1) + data = infoReader.read() + items = data.split('[') + for item in items: + splito = item.split(':') + DB[splito[0]] =splito[1] + return DB diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4pcutils.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4pcutils.py index efc310d..d460a70 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4pcutils.py +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4pcutils.py @@ -93,18 +93,25 @@ def CryptUnprotectData(): return CryptUnprotectData CryptUnprotectData = CryptUnprotectData() -# -# Locate and open the Kindle.info file. -# -def openKindleInfo(kInfoFile=None): - if kInfoFile == None: - regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\") - path = winreg.QueryValueEx(regkey, 'Local AppData')[0] - kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info' - if not os.path.isfile(kinfopath): - raise DrmException('Error: kindle.info file can not be found') - return open(kinfopath,'r') +# Locate the .kindle-info files +def getKindleInfoFiles(kInfoFiles): + regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\") + path = winreg.QueryValueEx(regkey, 'Local AppData')[0] + kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info' + if not os.path.isfile(kinfopath): + print('The kindle.info files has not been found.') else: - if not os.path.isfile(kInfoFile): - raise DrmException('Error: kindle.info file can not be found') - return open(kInfoFile, 'r') + kInfoFiles.append(kinfopath) + return kInfoFiles + +# Parse the Kindle.info file and return the records as a list of key-values +def parseKindleInfo(kInfoFile): + DB = {} + infoReader = open(kInfoFile, 'r') + infoReader.read(1) + data = infoReader.read() + items = data.split('{') + for item in items: + splito = item.split(':') + DB[splito[0]] =splito[1] + return DB diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/kgenpids.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/kgenpids.py index 039daf9..c10f105 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/kgenpids.py +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/kgenpids.py @@ -18,9 +18,9 @@ global charMap3 global charMap4 if sys.platform.startswith('win'): - from k4pcutils import openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap2 + from k4pcutils import getKindleInfoFiles, parseKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap2 if sys.platform.startswith('darwin'): - from k4mutils import openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap2 + from k4mutils import getKindleInfoFiles, parseKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap2 charMap1 = "n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M" charMap3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" @@ -67,22 +67,6 @@ def decode(data,map): result += pack("B",value) return result - -# Parse the Kindle.info file and return the records as a list of key-values -def parseKindleInfo(kInfoFile): - DB = {} - infoReader = openKindleInfo(kInfoFile) - infoReader.read(1) - data = infoReader.read() - if sys.platform.startswith('win'): - items = data.split('{') - else : - items = data.split('[') - for item in items: - splito = item.split(':') - DB[splito[0]] =splito[1] - return DB - # Get a record from the Kindle.info file for the key "hashedKey" (already hashed and encoded). # Return the decoded and decrypted record def getKindleInfoValueForHash(hashedKey): @@ -241,7 +225,7 @@ def getKindlePid(pidlst, rec209, token, serialnum): # Parse the EXTH header records and parse the Kindleinfo # file to calculate the book pid. -def getK4Pids(pidlst, rec209, token, kInfoFile=None): +def getK4Pids(pidlst, rec209, token, kInfoFile): global kindleDatabase global charMap1 kindleDatabase = None @@ -254,10 +238,17 @@ def getK4Pids(pidlst, rec209, token, kInfoFile=None): if kindleDatabase == None : return pidlst + + try: + # Get the Mazama Random number + MazamaRandomNumber = getKindleInfoValueForKey("MazamaRandomNumber") - # Get the Mazama Random number - MazamaRandomNumber = getKindleInfoValueForKey("MazamaRandomNumber") - + # Get the kindle account token + kindleAccountToken = getKindleInfoValueForKey("kindle.account.tokens") + except KeyError: + print "Keys not found in " + kInfoFile + return pidlst + # Get the HDD serial encodedSystemVolumeSerialNumber = encodeHash(GetVolumeSerialNumber(),charMap1) @@ -273,10 +264,7 @@ def getK4Pids(pidlst, rec209, token, kInfoFile=None): devicePID = checksumPid(devicePID) pidlst.append(devicePID) - # Compute book PID - - # Get the kindle account token - kindleAccountToken = getKindleInfoValueForKey("kindle.account.tokens") + # Compute book PIDs # book pid pidHash = SHA1(DSN+kindleAccountToken+rec209+token) @@ -300,8 +288,10 @@ def getK4Pids(pidlst, rec209, token, kInfoFile=None): def getPidList(md1, md2, k4, pids, serials, kInfoFiles): pidlst = [] + if kInfoFiles is None: + kInfoFiles = [] if k4: - pidlst = getK4Pids(pidlst, md1, md2) + kInfoFiles = getKindleInfoFiles(kInfoFiles) for infoFile in kInfoFiles: pidlst = getK4Pids(pidlst, md1, md2, infoFile) for serialnum in serials: diff --git a/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/genbook.py b/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/genbook.py index 19be51a..11258d1 100644 --- a/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/genbook.py +++ b/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/genbook.py @@ -323,12 +323,12 @@ def generateBook(bookDir, raw, fixedimage): meta_array = getMetaArray(metaFile) # replace special chars in title and authors like & < > - title = meta_array['Title'] + title = meta_array.get('Title','No Title Provided') title = title.replace('&','&') title = title.replace('<','<') title = title.replace('>','>') meta_array['Title'] = title - authors = meta_array['Authors'] + authors = meta_array.get('Authors','No Authors Provided') authors = authors.replace('&','&') authors = authors.replace('<','<') authors = authors.replace('>','>') @@ -413,8 +413,10 @@ def generateBook(bookDir, raw, fixedimage): htmlstr += '' + meta_array['Title'] + ' by ' + meta_array['Authors'] + '\n' htmlstr += '\n' htmlstr += '\n' - htmlstr += '\n' - htmlstr += '\n' + if 'ASIN' in meta_array: + htmlstr += '\n' + if 'GUID' in meta_array: + htmlstr += '\n' htmlstr += '\n' htmlstr += '\n\n' @@ -430,8 +432,10 @@ def generateBook(bookDir, raw, fixedimage): svgindex += '' + meta_array['Title'] + '\n' svgindex += '\n' svgindex += '\n' - svgindex += '\n' - svgindex += '\n' + if 'ASIN' in meta_array: + svgindex += '\n' + if 'GUID' in meta_array: + svgindex += '\n' svgindex += '\n' svgindex += '\n' @@ -485,9 +489,12 @@ def generateBook(bookDir, raw, fixedimage): opfstr += '\n' # adding metadata opfstr += ' \n' - opfstr += ' ' + meta_array['GUID'] + '\n' - opfstr += ' ' + meta_array['ASIN'] + '\n' - opfstr += ' ' + meta_array['oASIN'] + '\n' + if 'GUID' in meta_array: + opfstr += ' ' + meta_array['GUID'] + '\n' + if 'ASIN' in meta_array: + opfstr += ' ' + meta_array['ASIN'] + '\n' + if 'oASIN' in meta_array: + opfstr += ' ' + meta_array['oASIN'] + '\n' opfstr += ' ' + meta_array['Title'] + '\n' opfstr += ' ' + meta_array['Authors'] + '\n' opfstr += ' en\n' diff --git a/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/k4mobidedrm.py b/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/k4mobidedrm.py index d165f37..3ae1afa 100644 --- a/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/k4mobidedrm.py +++ b/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/k4mobidedrm.py @@ -29,7 +29,7 @@ from __future__ import with_statement # and import that ZIP into Calibre using its plugin configuration GUI. -__version__ = '2.7' +__version__ = '2.8' class Unbuffered: def __init__(self, stream): @@ -250,7 +250,7 @@ if not __name__ == "__main__" and inCalibre: Provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc.' supported_platforms = ['osx', 'windows', 'linux'] # Platforms this plugin will run on author = 'DiapDealer, SomeUpdates' # The author of this plugin - version = (0, 2, 7) # The version number of this plugin + version = (0, 2, 8) # The version number of this plugin file_types = set(['prc','mobi','azw','azw1','tpz']) # The file types that this plugin will be applied to on_import = True # Run this plugin during the import priority = 210 # run this plugin before mobidedrm, k4pcdedrm, k4dedrm diff --git a/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/k4mutils.py b/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/k4mutils.py index 5e57701..539723d 100644 --- a/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/k4mutils.py +++ b/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/k4mutils.py @@ -168,27 +168,33 @@ def CryptUnprotectData(encryptedData): return cleartext -# Locate and open the .kindle-info file -def openKindleInfo(kInfoFile=None): - if kInfoFile == None: - home = os.getenv('HOME') - cmdline = 'find "' + home + '/Library/Application Support" -name ".kindle-info"' - cmdline = cmdline.encode(sys.getfilesystemencoding()) - p1 = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False) - out1, out2 = p1.communicate() - reslst = out1.split('\n') - kinfopath = 'NONE' - cnt = len(reslst) - for j in xrange(cnt): - resline = reslst[j] - pp = resline.find('.kindle-info') - if pp >= 0: - kinfopath = resline - break - if not os.path.isfile(kinfopath): - raise DrmException('Error: .kindle-info file can not be found') - return open(kinfopath,'r') - else: - if not os.path.isfile(kInfoFile): - raise DrmException('Error: kindle-info file can not be found') - return open(kInfoFile, 'r') +# Locate the .kindle-info files +def getKindleInfoFiles(kInfoFiles): + home = os.getenv('HOME') + cmdline = 'find "' + home + '/Library/Application Support" -name ".kindle-info"' + cmdline = cmdline.encode(sys.getfilesystemencoding()) + p1 = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False) + out1, out2 = p1.communicate() + reslst = out1.split('\n') + kinfopath = 'NONE' + found = False + cnt = len(reslst) + for resline in reslst: + if os.path.isfile(resline): + kInfoFiles.append(resline) + found = True + if not found: + print('No .kindle-info files have been found.') + return kInfoFiles + +# Parse the Kindle.info file and return the records as a list of key-values +def parseKindleInfo(kInfoFile): + DB = {} + infoReader = open(kInfoFile, 'r') + infoReader.read(1) + data = infoReader.read() + items = data.split('[') + for item in items: + splito = item.split(':') + DB[splito[0]] =splito[1] + return DB diff --git a/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/k4pcutils.py b/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/k4pcutils.py index efc310d..d460a70 100644 --- a/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/k4pcutils.py +++ b/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/k4pcutils.py @@ -93,18 +93,25 @@ def CryptUnprotectData(): return CryptUnprotectData CryptUnprotectData = CryptUnprotectData() -# -# Locate and open the Kindle.info file. -# -def openKindleInfo(kInfoFile=None): - if kInfoFile == None: - regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\") - path = winreg.QueryValueEx(regkey, 'Local AppData')[0] - kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info' - if not os.path.isfile(kinfopath): - raise DrmException('Error: kindle.info file can not be found') - return open(kinfopath,'r') +# Locate the .kindle-info files +def getKindleInfoFiles(kInfoFiles): + regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\") + path = winreg.QueryValueEx(regkey, 'Local AppData')[0] + kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info' + if not os.path.isfile(kinfopath): + print('The kindle.info files has not been found.') else: - if not os.path.isfile(kInfoFile): - raise DrmException('Error: kindle.info file can not be found') - return open(kInfoFile, 'r') + kInfoFiles.append(kinfopath) + return kInfoFiles + +# Parse the Kindle.info file and return the records as a list of key-values +def parseKindleInfo(kInfoFile): + DB = {} + infoReader = open(kInfoFile, 'r') + infoReader.read(1) + data = infoReader.read() + items = data.split('{') + for item in items: + splito = item.split(':') + DB[splito[0]] =splito[1] + return DB diff --git a/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/kgenpids.py b/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/kgenpids.py index 039daf9..c10f105 100644 --- a/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/kgenpids.py +++ b/DeDRM_Windows_Application/DeDRM_WinApp/DeDRM_lib/lib/kgenpids.py @@ -18,9 +18,9 @@ global charMap3 global charMap4 if sys.platform.startswith('win'): - from k4pcutils import openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap2 + from k4pcutils import getKindleInfoFiles, parseKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap2 if sys.platform.startswith('darwin'): - from k4mutils import openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap2 + from k4mutils import getKindleInfoFiles, parseKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap2 charMap1 = "n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M" charMap3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" @@ -67,22 +67,6 @@ def decode(data,map): result += pack("B",value) return result - -# Parse the Kindle.info file and return the records as a list of key-values -def parseKindleInfo(kInfoFile): - DB = {} - infoReader = openKindleInfo(kInfoFile) - infoReader.read(1) - data = infoReader.read() - if sys.platform.startswith('win'): - items = data.split('{') - else : - items = data.split('[') - for item in items: - splito = item.split(':') - DB[splito[0]] =splito[1] - return DB - # Get a record from the Kindle.info file for the key "hashedKey" (already hashed and encoded). # Return the decoded and decrypted record def getKindleInfoValueForHash(hashedKey): @@ -241,7 +225,7 @@ def getKindlePid(pidlst, rec209, token, serialnum): # Parse the EXTH header records and parse the Kindleinfo # file to calculate the book pid. -def getK4Pids(pidlst, rec209, token, kInfoFile=None): +def getK4Pids(pidlst, rec209, token, kInfoFile): global kindleDatabase global charMap1 kindleDatabase = None @@ -254,10 +238,17 @@ def getK4Pids(pidlst, rec209, token, kInfoFile=None): if kindleDatabase == None : return pidlst + + try: + # Get the Mazama Random number + MazamaRandomNumber = getKindleInfoValueForKey("MazamaRandomNumber") - # Get the Mazama Random number - MazamaRandomNumber = getKindleInfoValueForKey("MazamaRandomNumber") - + # Get the kindle account token + kindleAccountToken = getKindleInfoValueForKey("kindle.account.tokens") + except KeyError: + print "Keys not found in " + kInfoFile + return pidlst + # Get the HDD serial encodedSystemVolumeSerialNumber = encodeHash(GetVolumeSerialNumber(),charMap1) @@ -273,10 +264,7 @@ def getK4Pids(pidlst, rec209, token, kInfoFile=None): devicePID = checksumPid(devicePID) pidlst.append(devicePID) - # Compute book PID - - # Get the kindle account token - kindleAccountToken = getKindleInfoValueForKey("kindle.account.tokens") + # Compute book PIDs # book pid pidHash = SHA1(DSN+kindleAccountToken+rec209+token) @@ -300,8 +288,10 @@ def getK4Pids(pidlst, rec209, token, kInfoFile=None): def getPidList(md1, md2, k4, pids, serials, kInfoFiles): pidlst = [] + if kInfoFiles is None: + kInfoFiles = [] if k4: - pidlst = getK4Pids(pidlst, md1, md2) + kInfoFiles = getKindleInfoFiles(kInfoFiles) for infoFile in kInfoFiles: pidlst = getK4Pids(pidlst, md1, md2, infoFile) for serialnum in serials: diff --git a/KindleBooks_Tools/KindleBooks/lib/genbook.py b/KindleBooks_Tools/KindleBooks/lib/genbook.py index 19be51a..11258d1 100644 --- a/KindleBooks_Tools/KindleBooks/lib/genbook.py +++ b/KindleBooks_Tools/KindleBooks/lib/genbook.py @@ -323,12 +323,12 @@ def generateBook(bookDir, raw, fixedimage): meta_array = getMetaArray(metaFile) # replace special chars in title and authors like & < > - title = meta_array['Title'] + title = meta_array.get('Title','No Title Provided') title = title.replace('&','&') title = title.replace('<','<') title = title.replace('>','>') meta_array['Title'] = title - authors = meta_array['Authors'] + authors = meta_array.get('Authors','No Authors Provided') authors = authors.replace('&','&') authors = authors.replace('<','<') authors = authors.replace('>','>') @@ -413,8 +413,10 @@ def generateBook(bookDir, raw, fixedimage): htmlstr += '' + meta_array['Title'] + ' by ' + meta_array['Authors'] + '\n' htmlstr += '\n' htmlstr += '\n' - htmlstr += '\n' - htmlstr += '\n' + if 'ASIN' in meta_array: + htmlstr += '\n' + if 'GUID' in meta_array: + htmlstr += '\n' htmlstr += '\n' htmlstr += '\n\n' @@ -430,8 +432,10 @@ def generateBook(bookDir, raw, fixedimage): svgindex += '' + meta_array['Title'] + '\n' svgindex += '\n' svgindex += '\n' - svgindex += '\n' - svgindex += '\n' + if 'ASIN' in meta_array: + svgindex += '\n' + if 'GUID' in meta_array: + svgindex += '\n' svgindex += '\n' svgindex += '\n' @@ -485,9 +489,12 @@ def generateBook(bookDir, raw, fixedimage): opfstr += '\n' # adding metadata opfstr += ' \n' - opfstr += ' ' + meta_array['GUID'] + '\n' - opfstr += ' ' + meta_array['ASIN'] + '\n' - opfstr += ' ' + meta_array['oASIN'] + '\n' + if 'GUID' in meta_array: + opfstr += ' ' + meta_array['GUID'] + '\n' + if 'ASIN' in meta_array: + opfstr += ' ' + meta_array['ASIN'] + '\n' + if 'oASIN' in meta_array: + opfstr += ' ' + meta_array['oASIN'] + '\n' opfstr += ' ' + meta_array['Title'] + '\n' opfstr += ' ' + meta_array['Authors'] + '\n' opfstr += ' en\n' diff --git a/KindleBooks_Tools/KindleBooks/lib/k4mobidedrm.py b/KindleBooks_Tools/KindleBooks/lib/k4mobidedrm.py index d165f37..3ae1afa 100644 --- a/KindleBooks_Tools/KindleBooks/lib/k4mobidedrm.py +++ b/KindleBooks_Tools/KindleBooks/lib/k4mobidedrm.py @@ -29,7 +29,7 @@ from __future__ import with_statement # and import that ZIP into Calibre using its plugin configuration GUI. -__version__ = '2.7' +__version__ = '2.8' class Unbuffered: def __init__(self, stream): @@ -250,7 +250,7 @@ if not __name__ == "__main__" and inCalibre: Provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc.' supported_platforms = ['osx', 'windows', 'linux'] # Platforms this plugin will run on author = 'DiapDealer, SomeUpdates' # The author of this plugin - version = (0, 2, 7) # The version number of this plugin + version = (0, 2, 8) # The version number of this plugin file_types = set(['prc','mobi','azw','azw1','tpz']) # The file types that this plugin will be applied to on_import = True # Run this plugin during the import priority = 210 # run this plugin before mobidedrm, k4pcdedrm, k4dedrm diff --git a/KindleBooks_Tools/KindleBooks/lib/k4mutils.py b/KindleBooks_Tools/KindleBooks/lib/k4mutils.py index 5e57701..539723d 100644 --- a/KindleBooks_Tools/KindleBooks/lib/k4mutils.py +++ b/KindleBooks_Tools/KindleBooks/lib/k4mutils.py @@ -168,27 +168,33 @@ def CryptUnprotectData(encryptedData): return cleartext -# Locate and open the .kindle-info file -def openKindleInfo(kInfoFile=None): - if kInfoFile == None: - home = os.getenv('HOME') - cmdline = 'find "' + home + '/Library/Application Support" -name ".kindle-info"' - cmdline = cmdline.encode(sys.getfilesystemencoding()) - p1 = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False) - out1, out2 = p1.communicate() - reslst = out1.split('\n') - kinfopath = 'NONE' - cnt = len(reslst) - for j in xrange(cnt): - resline = reslst[j] - pp = resline.find('.kindle-info') - if pp >= 0: - kinfopath = resline - break - if not os.path.isfile(kinfopath): - raise DrmException('Error: .kindle-info file can not be found') - return open(kinfopath,'r') - else: - if not os.path.isfile(kInfoFile): - raise DrmException('Error: kindle-info file can not be found') - return open(kInfoFile, 'r') +# Locate the .kindle-info files +def getKindleInfoFiles(kInfoFiles): + home = os.getenv('HOME') + cmdline = 'find "' + home + '/Library/Application Support" -name ".kindle-info"' + cmdline = cmdline.encode(sys.getfilesystemencoding()) + p1 = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False) + out1, out2 = p1.communicate() + reslst = out1.split('\n') + kinfopath = 'NONE' + found = False + cnt = len(reslst) + for resline in reslst: + if os.path.isfile(resline): + kInfoFiles.append(resline) + found = True + if not found: + print('No .kindle-info files have been found.') + return kInfoFiles + +# Parse the Kindle.info file and return the records as a list of key-values +def parseKindleInfo(kInfoFile): + DB = {} + infoReader = open(kInfoFile, 'r') + infoReader.read(1) + data = infoReader.read() + items = data.split('[') + for item in items: + splito = item.split(':') + DB[splito[0]] =splito[1] + return DB diff --git a/KindleBooks_Tools/KindleBooks/lib/k4pcutils.py b/KindleBooks_Tools/KindleBooks/lib/k4pcutils.py index efc310d..d460a70 100644 --- a/KindleBooks_Tools/KindleBooks/lib/k4pcutils.py +++ b/KindleBooks_Tools/KindleBooks/lib/k4pcutils.py @@ -93,18 +93,25 @@ def CryptUnprotectData(): return CryptUnprotectData CryptUnprotectData = CryptUnprotectData() -# -# Locate and open the Kindle.info file. -# -def openKindleInfo(kInfoFile=None): - if kInfoFile == None: - regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\") - path = winreg.QueryValueEx(regkey, 'Local AppData')[0] - kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info' - if not os.path.isfile(kinfopath): - raise DrmException('Error: kindle.info file can not be found') - return open(kinfopath,'r') +# Locate the .kindle-info files +def getKindleInfoFiles(kInfoFiles): + regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\") + path = winreg.QueryValueEx(regkey, 'Local AppData')[0] + kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info' + if not os.path.isfile(kinfopath): + print('The kindle.info files has not been found.') else: - if not os.path.isfile(kInfoFile): - raise DrmException('Error: kindle.info file can not be found') - return open(kInfoFile, 'r') + kInfoFiles.append(kinfopath) + return kInfoFiles + +# Parse the Kindle.info file and return the records as a list of key-values +def parseKindleInfo(kInfoFile): + DB = {} + infoReader = open(kInfoFile, 'r') + infoReader.read(1) + data = infoReader.read() + items = data.split('{') + for item in items: + splito = item.split(':') + DB[splito[0]] =splito[1] + return DB diff --git a/KindleBooks_Tools/KindleBooks/lib/kgenpids.py b/KindleBooks_Tools/KindleBooks/lib/kgenpids.py index 039daf9..c10f105 100644 --- a/KindleBooks_Tools/KindleBooks/lib/kgenpids.py +++ b/KindleBooks_Tools/KindleBooks/lib/kgenpids.py @@ -18,9 +18,9 @@ global charMap3 global charMap4 if sys.platform.startswith('win'): - from k4pcutils import openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap2 + from k4pcutils import getKindleInfoFiles, parseKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap2 if sys.platform.startswith('darwin'): - from k4mutils import openKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap2 + from k4mutils import getKindleInfoFiles, parseKindleInfo, CryptUnprotectData, GetUserName, GetVolumeSerialNumber, charMap2 charMap1 = "n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M" charMap3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" @@ -67,22 +67,6 @@ def decode(data,map): result += pack("B",value) return result - -# Parse the Kindle.info file and return the records as a list of key-values -def parseKindleInfo(kInfoFile): - DB = {} - infoReader = openKindleInfo(kInfoFile) - infoReader.read(1) - data = infoReader.read() - if sys.platform.startswith('win'): - items = data.split('{') - else : - items = data.split('[') - for item in items: - splito = item.split(':') - DB[splito[0]] =splito[1] - return DB - # Get a record from the Kindle.info file for the key "hashedKey" (already hashed and encoded). # Return the decoded and decrypted record def getKindleInfoValueForHash(hashedKey): @@ -241,7 +225,7 @@ def getKindlePid(pidlst, rec209, token, serialnum): # Parse the EXTH header records and parse the Kindleinfo # file to calculate the book pid. -def getK4Pids(pidlst, rec209, token, kInfoFile=None): +def getK4Pids(pidlst, rec209, token, kInfoFile): global kindleDatabase global charMap1 kindleDatabase = None @@ -254,10 +238,17 @@ def getK4Pids(pidlst, rec209, token, kInfoFile=None): if kindleDatabase == None : return pidlst + + try: + # Get the Mazama Random number + MazamaRandomNumber = getKindleInfoValueForKey("MazamaRandomNumber") - # Get the Mazama Random number - MazamaRandomNumber = getKindleInfoValueForKey("MazamaRandomNumber") - + # Get the kindle account token + kindleAccountToken = getKindleInfoValueForKey("kindle.account.tokens") + except KeyError: + print "Keys not found in " + kInfoFile + return pidlst + # Get the HDD serial encodedSystemVolumeSerialNumber = encodeHash(GetVolumeSerialNumber(),charMap1) @@ -273,10 +264,7 @@ def getK4Pids(pidlst, rec209, token, kInfoFile=None): devicePID = checksumPid(devicePID) pidlst.append(devicePID) - # Compute book PID - - # Get the kindle account token - kindleAccountToken = getKindleInfoValueForKey("kindle.account.tokens") + # Compute book PIDs # book pid pidHash = SHA1(DSN+kindleAccountToken+rec209+token) @@ -300,8 +288,10 @@ def getK4Pids(pidlst, rec209, token, kInfoFile=None): def getPidList(md1, md2, k4, pids, serials, kInfoFiles): pidlst = [] + if kInfoFiles is None: + kInfoFiles = [] if k4: - pidlst = getK4Pids(pidlst, md1, md2) + kInfoFiles = getKindleInfoFiles(kInfoFiles) for infoFile in kInfoFiles: pidlst = getK4Pids(pidlst, md1, md2, infoFile) for serialnum in serials: