property prefsFileName : "com.apprenticealf.dedrm.plist" property tempFileName : "DeDRM_temp.log" property prefsFolderName : "com.apprenticealf.dedrm" property newKeysFolderName : "newKeys" property handledExtensions : {"epub", "pdf", "prc", "azw", "azw1", "tpz", "azw3", "azw4", "mobi", "pobi", "pdb"} global python global oldAppleScript global eReaderTool global MobipocketTool global KindlePIDTool global BNKeyGenTool global BNKeyFetchTool global BNePubTool global KindleKeyTool global NookKeyTool global AdobeKeyGenTool global AdobeePubTool global ePubTestTool global AdobePDFTool global ZipFixTool global ProgressApp global PIDs global UDIDs global bnKeys global KindleSerialList global AndroidKeyList global KindleKeyList global AdeptKeyList global ErrorList global WarningList global CompletedList global ErrorCount global WarningCount global CompletedCount global totalebooks global completedebooks global outputFolder global logFilePath global tempfilepath global paraend on writetolog(logstring) try set fileRef to open for access logFilePath with write permission write logstring & " " to fileRef starting at eof as «class utf8» close access fileRef end try end writetolog on clearlog() try set fileRef to open for access logFilePath with write permission set eof fileRef to 0 close access fileRef end try end clearlog on cleartemp() try set fileRef to open for access tempfilepath with write permission set eof fileRef to 0 close access fileRef end try end cleartemp on readtemp() set tempContents to "" try set fileRef to open for access tempfilepath if (get eof fileRef) > 0 then set tempContents to read fileRef from 1 as «class utf8» end if close access fileRef end try return tempContents end readtemp on readtemplist() set templist to {} try set fileRef to open for access tempfilepath if (get eof fileRef) > 0 then set tempContents to paragraphs of (read fileRef from 1 as «class utf8») end if close access fileRef end try repeat with nextLine in tempContents if length of nextLine is greater than 0 then copy nextLine to the end of templist end if end repeat return templist end readtemplist on quotedtemppath() set tempFile to POSIX path of file tempfilepath return quoted form of tempFile end quotedtemppath on folderexists(inputFolder) try --display dialog "test -d " & quoted form of (POSIX path of inputFolder) do shell script "test -d " & quoted form of (POSIX path of inputFolder) return true end try return false end folderexists on fileexists(inputFile) try do shell script "test -f " & quoted form of (POSIX path of inputFile) return true end try return false end fileexists on GetFileExtension(fileName) set fileExtension to "" if fileName contains "." then set oldTIDs to AppleScript's text item delimiters set AppleScript's text item delimiters to "." set fileExtension to the last text item of fileName set AppleScript's text item delimiters to oldTIDs end if return fileExtension end GetFileExtension on GetFileNameOnly(fileName) set fileNameOnly to fileName if fileName contains "." then set oldTIDs to AppleScript's text item delimiters set AppleScript's text item delimiters to "." set fileNameOnly to (text items 1 through -2 of fileName) as string set AppleScript's text item delimiters to oldTIDs end if return fileNameOnly end GetFileNameOnly on GetTools() set paraend to " " set ASnumber to (text 1 thru 3 of (AppleScript's version as text) as real) set oldAppleScript to (ASnumber < 2.0) set python to "/usr/local/bin/python" if not fileexists(python) then set python to "/usr/bin/python" end if set pythonversion to "2.3" try set pythonversion to do shell script python & " -c \"from sys import version; print version[:3]\"" end try -- display dialog pythonversion if (pythonversion is not "2.5") and (pythonversion is not "2.6") and (pythonversion is not "2.7") then set dialogresult to (display dialog "This AppleScript requires Python 2.7x. Please install python 2.7x and try again." buttons {"Quit"} default button 1 with title "DeDRM" with icon stop) return false end if -- we always want a space after the command set python to python & " " set eReaderTool to POSIX path of (path to resource "erdr2pml.py") set MobipocketTool to POSIX path of (path to resource "k4mobidedrm.py") set KindlePIDTool to POSIX path of (path to resource "kindlepid.py") set BNKeyGenTool to POSIX path of (path to resource "ignoblekeygen.py") set BNKeyFetchTool to POSIX path of (path to resource "ignoblekeyfetch.py") set BNePubTool to POSIX path of (path to resource "ignobleepub.py") set KindleKeyTool to POSIX path of (path to resource "kindlekey.py") set NookKeyTool to POSIX path of (path to resource "ignoblekey.py") set AdobeKeyGenTool to POSIX path of (path to resource "adobekey.py") set AdobeePubTool to POSIX path of (path to resource "ineptepub.py") set ePubTestTool to POSIX path of (path to resource "epubtest.py") set AdobePDFTool to POSIX path of (path to resource "ineptpdf.py") set ZipFixTool to POSIX path of (path to resource "zipfix.py") set ProgressApp to POSIX path of (path to resource "DeDRM Progress.app") if not fileexists(eReaderTool) then set dialogresult to (display dialog "The eReader script (erdr2pml.py) is missing from this package. Get a fresh copy." buttons {"Quit", "Continue Anyway"} default button 1 with title "DeDRM" with icon stop) if button returned of dialogresult is "Quit" then return false end if end if if not fileexists(MobipocketTool) then --display dialog MobipocketTool set dialogresult to (display dialog "The Mobipocket script (k4mobidedrm.py) is missing from this package. Get a fresh copy." buttons {"Quit", "Continue Anyway"} default button 1 with title "DeDRM" with icon stop) if button returned of dialogresult is "Quit" then return false end if end if if not fileexists(KindlePIDTool) then --display dialog KindlePIDTool set dialogresult to (display dialog "The KindlePID script (kindlepid.py) is missing from this package. Get a fresh copy." buttons {"Quit", "Continue Anyway"} default button 1 with title "DeDRM" with icon stop) if button returned of dialogresult is "Quit" then return false end if end if if not fileexists(BNKeyGenTool) then set dialogresult to (display dialog "The B&N Key Generation script (ignoblekeygen.py) is missing from this package. Get a fresh copy." buttons {"Quit", "Continue Anyway"} default button 1 with title "DeDRM" with icon stop) if button returned of dialogresult is "Quit" then return false end if end if if not fileexists(BNKeyFetchTool) then set dialogresult to (display dialog "The B&N Key Fetch script (ignoblekeyfetch.py) is missing from this package. Get a fresh copy." buttons {"Quit", "Continue Anyway"} default button 1 with title "DeDRM" with icon stop) if button returned of dialogresult is "Quit" then return false end if end if if not fileexists(BNePubTool) then set dialogresult to (display dialog "The B&N script (ignobleepub.py) is missing from this package. Get a fresh copy." buttons {"Quit", "Continue Anyway"} default button 1 with title "DeDRM" with icon stop) if button returned of dialogresult is "Quit" then return false end if end if if not fileexists(NookKeyTool) then set dialogresult to (display dialog "The B&N Key Retrieval script (ignoblekey.py) is missing from this package. Get a fresh copy." buttons {"Quit", "Continue Anyway"} default button 1 with title "DeDRM" with icon stop) if button returned of dialogresult is "Quit" then return false end if end if if not fileexists(KindleKeyTool) then set dialogresult to (display dialog "The Kindle Key Generation script (kindlekey.py) is missing from this package. Get a fresh copy." buttons {"Quit", "Continue Anyway"} default button 1 with title "DeDRM" with icon stop) if button returned of dialogresult is "Quit" then return false end if end if if not fileexists(AdobeKeyGenTool) then set dialogresult to (display dialog "The Adobe Key Generation script (adobekey.py) is missing from this package. Get a fresh copy." buttons {"Quit", "Continue Anyway"} default button 1 with title "DeDRM" with icon stop) if button returned of dialogresult is "Quit" then return false end if end if if not fileexists(AdobePDFTool) then set dialogresult to (display dialog "The Adobe PDF script (ineptpdf.py) is missing from this package. Get a fresh copy." buttons {"Quit", "Continue Anyway"} default button 1 with title "DeDRM" with icon stop) if button returned of dialogresult is "Quit" then return false end if end if if not fileexists(AdobeePubTool) then set dialogresult to (display dialog "The Adobe ePub script (ineptepub.py) is missing from this package. Get a fresh copy." buttons {"Quit", "Continue Anyway"} default button 1 with title "DeDRM" with icon stop) if button returned of dialogresult is "Quit" then return false end if end if if not fileexists(ePubTestTool) then set dialogresult to (display dialog "The ePub encryption test script (epubtesttool.py) is missing from this package. Get a fresh copy." buttons {"Quit", "Continue Anyway"} default button 1 with title "DeDRM" with icon stop) if button returned of dialogresult is "Quit" then return false end if end if if not folderexists(ProgressApp) then set dialogresult to (display dialog "The Progress dialog application (DeDRM Progress.app) is missing from this package. Get a fresh copy." buttons {"Quit", "Continue Anyway"} default button 1 with title "DeDRM" with icon stop) if button returned of dialogresult is "Quit" then return false end if end if return true end GetTools on unlockmobifile(encryptedFile) --check it's a mobi file. set BOOKMOBI to "NOTAMOBI" try set BOOKMOBI to read file encryptedFile from 61 for 8 end try if BOOKMOBI is not "BOOKMOBI" and BOOKMOBI is not "TEXtREAd" then set TOPAZ to "NOT" try set TOPAZ to read file encryptedFile from 1 for 4 end try if TOPAZ is not "TPZ0" then set ErrorCount to ErrorCount + 1 set ErrorList to ErrorList & encryptedFile & " is neither a Kindle not a Mobipocket file. " return end if end if set encryptedFilePath to POSIX path of file encryptedFile tell application "Finder" set parent_folder to (container of file encryptedFile) as text set fileName to (name of file encryptedFile) as text end tell set fileExtension to "." & GetFileExtension(fileName) set fileName to GetFileNameOnly(fileName) if outputFolder is "" or not folderexists(outputFolder) then set dedrmFolder to POSIX path of file parent_folder else set dedrmFolder to POSIX path of file outputFolder end if set shellcommand to python & (quoted form of MobipocketTool) repeat with KindleKeyPath in KindleKeyList set shellcommand to shellcommand & " -k " & quoted form of KindleKeyPath end repeat repeat with AndroidKeyPath in AndroidKeyList set shellcommand to shellcommand & " -a " & quoted form of AndroidKeyPath end repeat set Serialstring to GetSerialstring() if Serialstring is not "" then set shellcommand to shellcommand & " -s " & quoted form of Serialstring set PIDstring to GetPIDstring() if PIDstring is "" then set PIDstring to GetUDIDPIDs() else set PIDstring to PIDstring & "," & GetUDIDPIDs() end if if PIDstring is not "" then set shellcommand to shellcommand & " -p " & quoted form of PIDstring set shellcommand to shellcommand & " " & (quoted form of encryptedFilePath) & " " & (quoted form of dedrmFolder) set shellcommand to shellcommand & " > " & quotedtemppath() --display dialog "shellcommand: " default answer shellcommand buttons {"OK"} default button 1 giving up after 10 writetolog("shellcommand: " & shellcommand) cleartemp() set DecodingError to false set shellresult to "" set ErrorText to "" set decoded to true try do shell script shellcommand on error ErrorText number errnum set DecodingError to true set decoded to false end try set shellresult to readtemp() writetolog("shellresult: " & shellresult & " " & ErrorText) if decoded then set decoded to ((offset of "No key found" in shellresult) is 0) end if if not decoded then DecodingError = true set newKeyList to GetKindleKeys() if length of newKeyList > 0 then set shellcommand to python & (quoted form of MobipocketTool) repeat with KindleKeyPath in newKeyList set shellcommand to shellcommand & " -k " & quoted form of KindleKeyPath end repeat set shellcommand to shellcommand & " " & (quoted form of encryptedFilePath) & " " & (quoted form of dedrmFolder) set shellcommand to shellcommand & " > " & quotedtemppath() --display dialog "shellcommand: " default answer shellcommand buttons {"OK"} default button 1 giving up after 10 writetolog("shellcommand: " & shellcommand) cleartemp() set DecodingError to false set shellresult to "" set ErrorText to "" set decoded to true try do shell script shellcommand on error ErrorText number errnum set DecodingError to true set decoded to false end try set shellresult to readtemp() writetolog("shellresult: " & shellresult & " " & ErrorText) if decoded then set DecodingError to not ((offset of "No key found" in shellresult) is 0) end if if not DecodingError then set KindleKeyList to KindleKeyList & newKeyList end if end if end if if DecodingError then set ErrorCount to ErrorCount + 1 set ErrorList to ErrorList & fileName & fileExtension & " couldn't be decrypted. " else if (offset of "not encrypted" in shellresult) > 0 then set WarningCount to WarningCount + 1 set WarningList to WarningList & fileName & fileExtension & " is not encrypted. " else set CompletedCount to CompletedCount + 1 set CompletedList to CompletedList & fileName & fileExtension & paraend end if end unlockmobifile on unlockpdbfile(encryptedFile) --check it's an eReader file. set PNRdPPrs to "NOT_eREADER" try set PNRdPPrs to read file encryptedFile from 61 for 8 end try if PNRdPPrs is not "PNRdPPrs" then set ErrorCount to ErrorCount + 1 set ErrorList to ErrorList & encryptedFile & " is not an eReader file. " return end if set encryptedFilePath to POSIX path of file encryptedFile tell application "Finder" set parent_folder to (container of file encryptedFile) as text set fileName to (name of file encryptedFile) as text end tell set fileExtension to "." & GetFileExtension(fileName) set fileName to GetFileNameOnly(fileName) if outputFolder is "" or not folderexists(outputFolder) then set dedrmFolder to parent_folder else set dedrmFolder to outputFolder end if set pmlFile to dedrmFolder & fileName & "_Source:" & fileName & ".pml" set clearFile to dedrmFolder & fileName & "_Source:" & fileName & ".pdb" set clearFileNew to dedrmFolder & fileName & "_Source:" & fileName & "_nodrm.pdb" set clearFileNewName to fileName & "_nodrm.pdb" set sourcePath to POSIX path of file dedrmFolder & fileName & "_Source/" set pmlzFilePath to POSIX path of file dedrmFolder & fileName & "_nodrm.pmlz" if length of GeteReaderKeystring() is 0 then GeteReaderKeys("DeDRM Configure for eReader") end if set shellresult to "Error: No eReader keys supplied. Can't decrypt." repeat with BNKey in bnKeys set encryptionName to first item of BNKey set encryptionNumber to second item of BNKey if length of encryptionName > 0 and length of encryptionNumber > 0 then set shellcommand to python & (quoted form of eReaderTool) & " " & (quoted form of encryptedFilePath) & " " & (quoted form of sourcePath) & " " & (quoted form of encryptionName) & " " & (quoted form of encryptionNumber) set shellcommand to shellcommand & " > " & quotedtemppath() --display dialog "shellcommand: " & shellcommand buttons {"OK"} default button 1 giving up after 10 writetolog("shellcommand: " & shellcommand) set shellresult to "no result" cleartemp() set DecodingError to false set ErrorText to "" try do shell script shellcommand on error ErrorText set DecodingError to true end try set shellresult to readtemp() writetolog("shellresult: " & shellresult & " " & ErrorText) --display dialog shellresult if not DecodingError then -- try to run dropbook and move compiled book to same folder as encrypted book try try tell application "Finder" to set DropBook to (application file id "PPDr") as string on error error "Couldn't find DropBook application" end try -- now also compile back up into pdb file using DropBook if it's on this system tell application DropBook to open file pmlFile delay 1 -- probably don't need this, but just in case. tell application DropBook to quit -- DropBook will quit only after the compilation has happened try tell application "Finder" set name of file clearFile to clearFileNewName move clearFileNew to dedrmFolder end tell on error errmsg error "DropBook failed to compile the source folder into a pdb file." end try on error errmsg set WarningCount to WarningCount + 1 set WarningList to (WarningList & fileName & fileExtension & " couldn't be compiled back to pdb: " & errmsg as text) & " " end try -- try to compress source folder contensts to a pmlz file for easy import into Calibre try set shellcommand to "cd " & quoted form of sourcePath & "; zip -r " & quoted form of pmlzFilePath & " *" --display dialog shellcommand writetolog("shellcommand: " & shellcommand) set zipshellresult to "" set zipshellresult to do shell script shellcommand writetolog("shellresult: " & zipshellresult) --display dialog zipshellresult tell application "Finder" -- move source folder to the trash move dedrmFolder & fileName & "_Source" to trash end tell on error errmsg set WarningCount to WarningCount + 1 set WarningList to (WarningList & fileName & fileExtension & "source folder couldn't be compressed to pmlz: " & errmsg as text) & " " end try -- we've done the decrypting so exit the loop. exit repeat end if end if end repeat try if (offset of "incorrect eReader version 10" in shellresult) > 0 then set WarningCount to WarningCount + 1 set WarningList to WarningList & fileName & fileExtension & " is not encrypted. " else if DecodingError then set ErrorCount to ErrorCount + 1 set ErrorList to (ErrorList & fileName & fileExtension & " couldn't be decoded: " & ErrorText as text) & " " else set CompletedCount to CompletedCount + 1 set CompletedList to CompletedList & fileName & fileExtension & paraend end if end try end unlockpdbfile on unlockepubfile(encryptedFile) set encryptedFilePath to POSIX path of file encryptedFile tell application "Finder" set parent_folder to (container of file encryptedFile) as text set fileName to (name of file encryptedFile) as text end tell set fileExtension to "." & GetFileExtension(fileName) set fileName to GetFileNameOnly(fileName) set decoded to false -- first we must fix any possible zip problems with the epub if outputFolder is "" or not folderexists(outputFolder) then set fixedFilePath to POSIX path of file (parent_folder & fileName & "_fixed" & fileExtension) else set fixedFilePath to POSIX path of file (outputFolder & fileName & "_fixed" & fileExtension) end if set shellcommand to python & (quoted form of ZipFixTool) & " " & (quoted form of encryptedFilePath) & " " & (quoted form of fixedFilePath) set shellcommand to shellcommand & " > " & quotedtemppath() writetolog("shellcommand: " & shellcommand) cleartemp() set ZipErrorText to "" set ZipFixError to false try --display dialog "shellcommand: " default answer shellcommand buttons {"OK"} default button 1 giving up after 10 do shell script shellcommand on error errmsg set ZipErrorText to errmsg set ZipFixError to true end try set shellresult to readtemp() writetolog("shellresult: " & shellresult & " " & ZipErrorText) --display dialog shellresult if ZipFixError then set ErrorCount to ErrorCount + 1 set ErrorList to ((ErrorList & fileName & fileExtension & " had a problem with ZipFix: " & ZipErrorText as text) & " ") return end if --check it's an ePub file. set ePubSig to "NOT_ePub" try set ePubSig to read POSIX file fixedFilePath from 31 for 28 end try --display dialog ePubSig if ePubSig is not "mimetypeapplication/epub+zip" then set ErrorCount to ErrorCount + 1 set ErrorList to ErrorList & encryptedFile & " is not an ePub file. " tell application "Finder" move alias ((POSIX file fixedFilePath) as text) to trash end tell return end if if outputFolder is "" or not folderexists(outputFolder) then set unlockedFilePath to POSIX path of file (parent_folder & fileName & "_nodrm" & fileExtension) else set unlockedFilePath to POSIX path of file (outputFolder & fileName & "_nodrm" & fileExtension) end if set shellresult to "no keys" set ErrorText to "" -- get encryption type set TryBandNePub to true set TryAdobeePub to true set shellcommand to python & (quoted form of ePubTestTool) & " " & (quoted form of fixedFilePath) set shellcommand to shellcommand & " > " & quotedtemppath() --display dialog "shellcommand: " & shellcommand buttons {"OK"} default button 1 giving up after 10 writetolog("shellcommand: " & shellcommand) cleartemp() set TestError to false set ErrorText to "" try do shell script shellcommand on error ErrorText set TestError to true end try set shellresult to readtemp() writetolog("shellresult: " & shellresult & " " & ErrorText) --display dialog shellresult if not TestError then if (offset of "B&N" in shellresult) > 0 then set TryAdobeePub to false else if (offset of "Adobe" in shellresult) > 0 then set TryBandNePub to false else if (offset of "Unencrypted" in shellresult) > 0 then set TryAdobeePub to false set TryBandNePub to false end if end if -- first we'll try the Barnes & Noble keys if TryBandNePub then set decoded to decodebandnepub(fixedFilePath, unlockedFilePath, bnKeys) if not decoded then set newKeyList to GetNookKeys() set decoded to decodebandnepub(fixedFilePath, unlockedFilePath, newKeyList) if decoded then set bnKeys to bnKeys & newKeyList end if end if end if if (not decoded) and TryAdobeePub then set decoded to decodeadobeepub(fixedFilePath, unlockedFilePath, AdeptKeyList) if not decoded then set newKeyList to GetAdeptKeys() set decoded to decodeadobeepub(fixedFilePath, unlockedFilePath, newKeyList) if decoded then set AdeptKeyList to AdeptKeyList & newKeyList end if end if end if if decoded then set CompletedCount to CompletedCount + 1 set CompletedList to CompletedList & fileName & fileExtension & paraend else if not TryAdobeePub and not TryBandNePub then set WarningCount to WarningCount + 1 set WarningList to (WarningList & fileName & " doesn't seem to be encrypted. ") else if shellresult is "no keys" then set ErrorCount to ErrorCount + 1 set ErrorList to (ErrorList & fileName & fileExtension & " couldn't be decoded: no keys. ") else set ErrorCount to ErrorCount + 1 set ErrorList to ((ErrorList & fileName & fileExtension & " couldn't be decoded: " & ErrorText as text) & " ") end if tell application "Finder" move alias ((POSIX file fixedFilePath) as text) to trash end tell end unlockepubfile on decodebandnepub(fixedFilePath, unlockedFilePath, keyList) set decoded to false repeat with BNKey in keyList set keyfilepath to third item of BNKey if length of keyfilepath > 0 then set shellcommand to python & (quoted form of BNePubTool) & " " & (quoted form of keyfilepath) & " " & (quoted form of fixedFilePath) & " " & (quoted form of unlockedFilePath) set shellcommand to shellcommand & " > " & quotedtemppath() --display dialog "shellcommand: " & shellcommand buttons {"OK"} default button 1 giving up after 10 writetolog("shellcommand: " & shellcommand) cleartemp() set DecodingError to false set ErrorText to "" try do shell script shellcommand on error ErrorText set DecodingError to true end try set shellresult to readtemp() writetolog("shellresult: " & shellresult & " " & ErrorText) --display dialog shellresult if not DecodingError then set decoded to true exit repeat end if end if end repeat return decoded end decodebandnepub on decodeadobeepub(fixedFilePath, unlockedFilePath, keyList) set decoded to false repeat with AdeptKey in keyList set shellcommand to python & (quoted form of AdobeePubTool) & " " & (quoted form of AdeptKey) & " " & (quoted form of fixedFilePath) & " " & (quoted form of unlockedFilePath) set shellcommand to shellcommand & " > " & quotedtemppath() --display dialog "shellcommand: " default answer shellcommand buttons {"OK"} default button 1 giving up after 10 writetolog("shellcommand: " & shellcommand) cleartemp() set DecodingError to false set ErrorText to "" try do shell script shellcommand on error ErrorText set DecodingError to true end try set shellresult to readtemp() as text writetolog("shellresult: " & shellresult & " " & ErrorText) --display dialog shellresult if not DecodingError then set decoded to true exit repeat end if end repeat return decoded end decodeadobeepub on unlockpdffile(encryptedFile) --check it's an ePub file. set PDFSig to "NOT_PDF" try set PDFSig to read file encryptedFile from 1 for 4 end try --display dialog PDFSig if PDFSig is not "%PDF" then set ErrorCount to ErrorCount + 1 set ErrorList to ErrorList & encryptedFile & " is not a PDF file. " return end if set encryptedFilePath to POSIX path of file encryptedFile tell application "Finder" set parent_folder to (container of file encryptedFile) as text set fileName to (name of file encryptedFile) as text end tell set fileExtension to "." & GetFileExtension(fileName) set fileName to GetFileNameOnly(fileName) set decoded to "NO" -- first we must check we have a PDF script if not fileexists(AdobePDFTool) then set ErrorCount to ErrorCount + 1 set ErrorList to ErrorList & encryptedFile & " is a PDF file and no ineptpdf script found. " return end if if outputFolder is "" or not folderexists(outputFolder) then set unlockedFilePath to POSIX path of file (parent_folder & fileName & "_nodrm" & fileExtension) else set unlockedFilePath to POSIX path of file (outputFolder & fileName & "_nodrm" & fileExtension) end if set decoded to false set shellresult to "no keys" set ErrorText to "" repeat with AdeptKey in AdeptKeyList set shellcommand to python & (quoted form of AdobePDFTool) & " " & (quoted form of AdeptKey) & " " & (quoted form of encryptedFilePath) & " " & (quoted form of unlockedFilePath) set shellcommand to shellcommand & " > " & quotedtemppath() --display dialog "shellcommand: " default answer shellcommand buttons {"OK"} default button 1 giving up after 10 writetolog("shellcommand: " & shellcommand) cleartemp() set DecodingError to false set ErrorText to "" try do shell script shellcommand on error ErrorText set DecodingError to true end try set shellresult to readtemp() writetolog("shellresult: " & shellresult & " " & ErrorText) --display dialog shellresult if not DecodingError then set decoded to true exit repeat end if end repeat if decoded then set CompletedCount to CompletedCount + 1 set CompletedList to CompletedList & fileName & fileExtension & paraend else if shellresult is "no keys" then set ErrorCount to ErrorCount + 1 set ErrorList to (ErrorList & fileName & fileExtension & " couldn't be decoded: no keys. ") else set ErrorCount to ErrorCount + 1 set ErrorList to ((ErrorList & fileName & fileExtension & " couldn't be decoded: " & ErrorText as text) & " ") end if end unlockpdffile on handlefile(droppedFile) tell application "Finder" set fileName to (name of file droppedFile) as text end tell writetolog("Processing file: " & fileName) set fileExtension to GetFileExtension(fileName) set fileName to GetFileNameOnly(fileName) if fileExtension is "prc" or fileExtension is "mobi" or fileExtension is "pobi" or fileExtension is "azw" or fileExtension is "azw1" or fileExtension is "tpz" or fileExtension is "azw3" or fileExtension is "azw4" then set completedebooks to completedebooks + 1 IncProgress(fileName, completedebooks) unlockmobifile(droppedFile as text) else if fileExtension is "pdb" then set completedebooks to completedebooks + 1 IncProgress(fileName, completedebooks) unlockpdbfile(droppedFile as text) else if fileExtension is "epub" then set completedebooks to completedebooks + 1 IncProgress(fileName, completedebooks) unlockepubfile(droppedFile as text) else if fileExtension is "pdf" then set completedebooks to completedebooks + 1 IncProgress(fileName, completedebooks) unlockpdffile(droppedFile as text) end if end handlefile on handlefolder(droppedFolder) tell application "Finder" to set fileList to (every file in folder droppedFolder) tell application "Finder" to set folderList to (every folder in folder droppedFolder) repeat with this_item in fileList if not ProgressActive() then exit repeat end if handlefile(this_item as text) end repeat repeat with this_item in folderList if not ProgressActive() then exit repeat end if handlefolder(this_item as text) end repeat end handlefolder on countfile(droppedFile) tell application "Finder" set fileName to (name of file droppedFile) as text end tell set fileExtension to GetFileExtension(fileName) set fileName to GetFileNameOnly(fileName) --display dialog "fileName: " & fileName & " --extension: " & fileExtension if fileExtension is in handledExtensions then set totalebooks to totalebooks + 1 end if end countfile on GetSerialstring() set Serialstring to "" repeat with Serial in KindleSerialList if Serialstring is "" then set Serialstring to Serial else set Serialstring to Serialstring & "," & Serial end if end repeat return Serialstring end GetSerialstring on GetPIDstring() set PIDstring to "" repeat with PID in PIDs if PIDstring is "" then set PIDstring to PID else set PIDstring to PIDstring & "," & PID end if end repeat return PIDstring end GetPIDstring on GetUDIDstring() set UDIDstring to "" repeat with UDIDPair in UDIDs if UDIDstring is "" then set UDIDstring to first item of UDIDPair else set UDIDstring to UDIDstring & "," & first item of UDIDPair end if end repeat return UDIDstring end GetUDIDstring on GetUDIDPIDs() set UDIDPIDs to "" repeat with UDIDPair in UDIDs if UDIDPIDs is "" then set UDIDPIDs to second item of UDIDPair else set UDIDPIDs to UDIDPIDs & "," & second item of UDIDPair end if end repeat return UDIDPIDs end GetUDIDPIDs on GetAdeptstring() set Adeptstring to "" repeat with AdeptKeyFile in AdeptKeyList set AppleTypeFile to POSIX file (AdeptKeyFile as text) tell application "Finder" set fileName to name of file (AppleTypeFile as text) end tell if Adeptstring is "" then set Adeptstring to fileName else set Adeptstring to Adeptstring & " " & fileName end if end repeat return Adeptstring end GetAdeptstring on GetAndroidstring() set Androidstring to "" repeat with AndroidKeyFile in AndroidKeyList set AppleTypeFile to POSIX file (AndroidKeyFile as text) tell application "Finder" set fileName to name of file (AppleTypeFile as text) end tell if Androidstring is "" then set Androidstring to fileName else set Androidstring to Androidstring & " " & fileName end if end repeat return Androidstring end GetAndroidstring on GetKindlestring() set Kindlestring to "" repeat with KindleKeyFile in KindleKeyList set AppleTypeFile to POSIX file (KindleKeyFile as text) tell application "Finder" set fileName to name of file (AppleTypeFile as text) end tell if Kindlestring is "" then set Kindlestring to fileName else set Kindlestring to Kindlestring & " " & fileName end if end repeat return Kindlestring end GetKindlestring on GeteReaderKeystring() set Keystring to "" repeat with key in bnKeys if length of first item of key > 0 and length of second item of key > 0 then if Keystring is "" then set Keystring to first item of key & ":" & second item of key else set Keystring to Keystring & " " & first item of key & ":" & second item of key end if end if end repeat return Keystring end GeteReaderKeystring on GetBNKeystring() set BNKeystring to "" repeat with BNKey in bnKeys if (length of BNKey > 2) and ((length of (third item of BNKey)) > 0) then if BNKeystring is "" then set BNKeystring to first item of BNKey else set BNKeystring to BNKeystring & " " & first item of BNKey end if end if end repeat return BNKeystring end GetBNKeystring on DeleteeReaderKeys() set newKeys to {} repeat with BNKey in bnKeys if length of third item of BNKey > 0 then set newKeys to newKeys & {{first item of BNKey, "", third item of BNKey}} end if end repeat set bnKeys to newKeys end DeleteeReaderKeys on DeleteBNKeys() set newKeys to {} repeat with BNKey in bnKeys if length of second item of BNKey > 0 then set newKeys to newKeys & {{first item of BNKey, second item of BNKey, ""}} end if end repeat set bnKeys to newKeys end DeleteBNKeys on GetUDIDs(DialogTitle) set UDID to "" repeat set UDIDstring to GetUDIDstring() if UDIDstring is "" then set DialogPrompt to "Enter any iPod/iPhone/iPad UDIDs (40 characters) one at a time. Note: This only works for older versions of Kindle for iOS." set FinishedButton to "Close" else set DialogPrompt to "Current UDIDs: " & UDIDstring & ". Enter any additional iPod/iPhone/iPad UDIDs (40 characters) one at a time:" set FinishedButton to "Close" end if set dialogresult to (display dialog DialogPrompt default answer UDID buttons {"Delete All", "Add", FinishedButton} with title DialogTitle default button 2) if button returned of dialogresult is "Add" then set UDID to text returned of dialogresult set UDIDlength to length of UDID if UDIDlength is 40 then set shellresult to "" set scriptError to "" set shellcommand to python & (quoted form of KindlePIDTool) & " " & (quoted form of UDID) writetolog("shellcommand: " & shellcommand) try set shellresult to do shell script shellcommand on error errmsg set shellresult to "" set scriptError to "KindlePID Script failed:" & " " & errmsg end try writetolog("shellresult: " & scriptError & " " & shellresult) --display dialog shellresult if shellresult is not "" then set PID to (text -10 thru -1 of shellresult) if UDIDs contains {{UDID, PID}} then display dialog "UDID " & UDID & " is already in your list of UDIDs." buttons {"OK"} default button 1 with title "DeDRM" with icon caution else display dialog "The equivalent UDID for UDID " & UDID & " is " & PID & ". Adding to list." buttons {"OK"} default button 1 with title "DeDRM" with icon note set UDIDs to UDIDs & {{UDID, PID}} set UDID to "" end if else writetolog(scriptError) display dialog "Error generating PID from this UDID, error message was: " & scriptError buttons {"OK"} default button 1 with title "DeDRM" with icon caution end if else display dialog "UDIDs must be 40 characters." buttons {"OK"} default button 1 with title "DeDRM" with icon caution end if else if button returned of dialogresult is "Delete All" then if UDIDstring is not "" then try set dialogresult to (display dialog "Are you sure you want to delete all stored UDIDs?" buttons {"Cancel", "Delete"} default button 1 with title "DeDRM") end try if button returned of dialogresult is "Delete" then set UDIDs to {} end if end if else set UDID to text returned of dialogresult if UDID is not "" then try set dialogresult to (display dialog "You entered some text, but didn't click Add. Are you sure you want to more on to the next dialog?" buttons {"Whoops", "Yes, Move on"} default button 1 with title "DeDRM") end try if button returned of dialogresult is "Yes, Move on" then exit repeat end if else exit repeat end if end if end repeat end GetUDIDs on GetPIDs(DialogTitle) set PID to "" repeat set PIDstring to GetPIDstring() if PIDstring is "" then set DialogPrompt to "Enter any Mobipocket PIDs one at a time:" set FinishedButton to "Close" else set DialogPrompt to "Current PIDs: " & PIDstring & ". Enter any additional Mobipocket PIDs one at a time:" set FinishedButton to "Close" end if set dialogresult to (display dialog DialogPrompt default answer PID buttons {"Delete All", "Add", FinishedButton} with title DialogTitle default button 2) if button returned of dialogresult is "Add" then set PID to text returned of dialogresult set PIDlength to length of PID if PIDlength is 8 or PIDlength is 10 then if PIDs contains PID then display dialog "PID" & PID & " is already in your list of PIDs." buttons {"OK"} default button 1 with title "DeDRM" with icon caution else set PIDs to PIDs & PID set PID to "" end if else display dialog "PIDs must be 8 or 10 characters long, and UDIDs must be 40 characters." buttons {"OK"} default button 1 with title "DeDRM" with icon caution end if else if button returned of dialogresult is "Delete All" then if PIDstring is not "" then try set dialogresult to (display dialog "Are you sure you want to delete all stored PIDs?" buttons {"Cancel", "Delete"} default button 1 with title "DeDRM") end try if button returned of dialogresult is "Delete" then set PIDs to {} end if end if else set PID to text returned of dialogresult if PID is not "" then try set dialogresult to (display dialog "You entered some text, but didn't click Add. Are you sure you want to more on to the next dialog?" buttons {"Whoops", "Yes, Move on"} default button 1 with title "DeDRM") end try if button returned of dialogresult is "Yes, Move on" then exit repeat end if else exit repeat end if end if end repeat end GetPIDs on GetSerials(DialogTitle) set Serial to "" repeat set Serialstring to GetSerialstring() if Serialstring is "" then set DialogPrompt to "Enter any Kindle (not Kindle Fire) Serial Numbers one at a time:" set FinishedButton to "Close" else set DialogPrompt to "Current Kindle Serial Numbers: " & Serialstring & ". Enter any additional Kindle (not Kindle Fire) Serial Numbers one at a time:" set FinishedButton to "Close" end if set dialogresult to (display dialog DialogPrompt default answer Serial buttons {"Delete All", "Add", FinishedButton} with title DialogTitle default button 2) if button returned of dialogresult is "Add" then set Serial to text returned of dialogresult set Seriallength to length of Serial if Seriallength is 16 then set KindleSerialList to KindleSerialList & Serial set Serial to "" else display dialog "Kindle Serial Numbers are 16 characters long." buttons {"OK"} default button 1 with title "DeDRM" with icon caution end if else if button returned of dialogresult is "Delete All" then if Serialstring is not "" then try set dialogresult to (display dialog "Are you sure you want to delete all stored Kindle Serial Numbers?" buttons {"Cancel", "Delete"} default button 1 with title "DeDRM") end try if button returned of dialogresult is "Delete" then set KindleSerialList to {} end if end if else set Serial to text returned of dialogresult if Serial is not "" then try set dialogresult to (display dialog "You entered some text, but didn't click Add. Are you sure you want to more on to the next dialog?" buttons {"Whoops", "Yes, Move on"} default button 1 with title "DeDRM") end try if button returned of dialogresult is "Yes, Move on" then exit repeat end if else exit repeat end if end if end repeat end GetSerials on GetOutputFolder(DialogTitle) set newFolder to "" try tell me to activate repeat if folderexists(outputFolder) then set DialogPrompt to "The deDRMed ebooks will be saved to the following folder: " & (outputFolder) & " " else set outputFolder to "" set DialogPrompt to "The deDRMed ebooks will be saved in the same folder as the original DRMed ebooks. " end if set DialogPrompt to DialogPrompt & "To keep this choice, click 'Close', otherwise select the option you want by clicking on one of the other buttons." set dialogresult to (display dialog DialogPrompt buttons {"Use Same Folder as DRMed Ebooks", "Choose Output Folder…", "Close"} with title DialogTitle default button 3) if button returned of dialogresult is "Choose Output Folder…" then try if outputFolder is "" then set outputFolder to ((choose folder with prompt "DRM Application Please choose the Output Folder for DeDRMed ebooks.") as text) else set outputFolder to ((choose folder with prompt "DRM Application Please choose the Output Folder for DeDRMed ebooks." default location alias outputFolder) as text) end if end try else if button returned of dialogresult is "Use Same Folder as DRMed Ebooks" then set outputFolder to "" else exit repeat end if --on error errormessage -- display dialog errormessage end repeat end try WritePrefs() end GetOutputFolder on GeteReaderKeys(DialogTitle) set keyText to "" repeat set Keystring to GeteReaderKeystring() if Keystring is "" then set DialogPrompt to "Please enter any " set FinishedButton to "Close" else set DialogPrompt to "eReader keys: " & Keystring & " Please enter any additional " set FinishedButton to "Close" end if set DialogPrompt to DialogPrompt & "eReader Name,Number key pairs one at a time. The name is usually your name as on your credit card, but might be your user name. Only the last 8 digits of the Number are needed. If you enter more digits, only the last eight will be stored or displayed. Please separate the name and number with a colon (:) and click \"Add\"." set dialogresult to (display dialog DialogPrompt default answer keyText with title DialogTitle buttons {"Delete All", "Add", FinishedButton} default button 2) if button returned of dialogresult is "Add" then set keyText to text returned of dialogresult if not (keyText contains ":") then display dialog "Name and Number must be separated by a colon (:)." buttons {"OK"} default button 1 with title "DeDRM" with icon caution else set oldTIDs to AppleScript's text item delimiters set AppleScript's text item delimiters to ":" set keyNumber to the last text item of keyText set keyName to (text items 1 through -2 of keyText) as string set AppleScript's text item delimiters to oldTIDs if ((length of keyNumber) = 16 or (length of keyNumber) = 15 or (length of keyNumber) = 8) and (length of keyName) > 0 then set bnKeys to bnKeys & {{keyName, (text -8 thru -1 of keyNumber), ""}} set keyText to "" else display dialog "Key numbers must be 8 or 15 or 16 characters long (no spaces) and the key name must not be absent." buttons {"OK"} default button 1 with title "DeDRM" with icon caution end if end if else if button returned of dialogresult is "Delete All" then if Keystring is not "" then try set dialogresult to (display dialog "Are you sure you want to delete all stored Name:Number key pairs?" buttons {"Cancel", "Delete"} default button 1 with title "DeDRM") end try if button returned of dialogresult is "Delete" then DeleteeReaderKeys() GetNookKey() end if end if set keyText to "" else set keyText to text returned of dialogresult if keyText is not "" then try set dialogresult to (display dialog "You entered some text, but didn't click Add. Are you sure you want to more on to the next dialog?" buttons {"Whoops", "Yes, Move on"} default button 1 with title "DeDRM") end try if button returned of dialogresult is "Yes, Move on" then exit repeat end if else exit repeat end if end if end repeat end GeteReaderKeys on GetBNKeys(DialogTitle) set bnKeyText to "" repeat set BNKeystring to GetBNKeystring() if BNKeystring is "" then set DialogPrompt to "Please enter any " set FinishedButton to "Close" else set DialogPrompt to "Current B&N/Nook keys: " & BNKeystring & " Please enter any additional " set FinishedButton to "Close" end if set DialogPrompt to DialogPrompt & "Barnes & Noble/Nook account email addresses and passwords one at a time. Please separate the address and password with a colon (:)and click \"Add\". Note that your account password will only be used to retrieve the key and will not be stored. *** Retrieving the account key may take a couple of minutes, please be patient. *** Or to add a an already generated .b64 file, just click \"Add\" with nothing in the text field." set dialogresult to (display dialog DialogPrompt default answer bnKeyText with title DialogTitle buttons {"Forget All", "Add", FinishedButton} default button 2) if button returned of dialogresult is "Add" then set bnKeyText to text returned of dialogresult if bnKeyText is "" then try set newFile to (choose file with prompt "Please select a Barnes & Noble (.b64) key file") as text -- copy to prefs AddbnKey(newFile) on error message if message does not contain "cancel" then display dialog message end if end try else if not (bnKeyText contains ":") then display dialog "Email address and password must be separated by a colon (:)." buttons {"OK"} default button 1 with title "DeDRM" with icon caution else set oldTIDs to AppleScript's text item delimiters set AppleScript's text item delimiters to ":" set keyEmail to the first text item of bnKeyText set keyPassword to (text items 2 through -1 of bnKeyText) as string set AppleScript's text item delimiters to oldTIDs if (keyEmail contains "@") and (length of keyPassword) > 0 then -- get the B&N key from this pair set shellresult to "" set scriptError to "" set success to true set prefsFolder to (path to preferences from user domain as string) & prefsFolderName & ":" set keyfileName to GetUniqueName(prefsFolder, keyEmail & ".b64") set keyfilepath to POSIX path of (prefsFolder & keyfileName) set shellcommand to python & (quoted form of BNKeyFetchTool) & " " & (quoted form of keyEmail) & " " & (quoted form of keyPassword) & " " & quoted form of keyfilepath set shellcommand to shellcommand & " > " & quotedtemppath() writetolog("shellcommand: " & shellcommand) cleartemp() try do shell script shellcommand on error scriptError set success to false end try set shellresult to readtemp() writetolog("shellresult: " & scriptError & " " & shellresult) --display dialog shellresult if success then set bnKeys to bnKeys & {{keyEmail, "", keyfilepath}} set bnKeyText to "" else display dialog "Error generating key from this info, error message was: " & scriptError buttons {"OK"} default button 1 with title "DeDRM" with icon caution end if else display dialog "Key numbers must be 8 or 15 or 16 characters long (no spaces) and the key name must not be absent." buttons {"OK"} default button 1 with title "DeDRM" with icon caution end if end if else if button returned of dialogresult is "Forget All" then if BNKeystring is not "" then try set dialogresult to (display dialog "Are you sure you want to forget all stored Barnes & Noble/Nook keys?" buttons {"Cancel", "Forget"} default button 1 with title "DeDRM") end try if button returned of dialogresult is "Forget" then DeleteBNKeys() end if end if set bnKeyText to "" else set bnKeyText to text returned of dialogresult if bnKeyText is not "" then try set dialogresult to (display dialog "You entered some text, but didn't click Add. Are you sure you want to more on to the next dialog?" buttons {"Whoops", "Yes, Move on"} default button 1 with title "DeDRM") end try if button returned of dialogresult is "Yes, Move on" then exit repeat end if else exit repeat end if end if end repeat end GetBNKeys on GetAdeptKeyFiles(DialogTitle) repeat set Adeptstring to GetAdeptstring() if Adeptstring is "" then set DialogPrompt to "To add Adobe Digital Editions (Adept) key files, click the Add… button. " set dialogresult to (display dialog DialogPrompt buttons {"Add…", "Close"} with title DialogTitle default button 1) else set DialogPrompt to "Current Adobe Digital Editions (Adept) key files: " & Adeptstring & " To add extra key files (.der), click the Add… button." set dialogresult to (display dialog DialogPrompt buttons {"Forget All", "Add…", "Close"} with title DialogTitle default button 2) end if if button returned of dialogresult is "Add…" then try set newFile to (choose file with prompt "Please select an Adobe Digital Editions (.der) key file") as text -- copy to prefs AddAdeptKey(newFile) on error message if message does not contain "cancel" then display dialog message end if end try else if button returned of dialogresult is "Forget All" then try set dialogresult to (display dialog "Are you sure you want to forget all extra Adobe Digital Editions Adept key files?" buttons {"Cancel", "Forget"} default button 1 with title "DeDRM") end try if button returned of dialogresult is "Forget" then set AdeptKeyList to {} end if else exit repeat end if end repeat end GetAdeptKeyFiles on GetKindleKeyFiles(DialogTitle) repeat set Kindlestring to GetKindlestring() if Kindlestring is "" then set DialogPrompt to "To add Kindle for Mac key files (.k4i), click the Add… button. " set FinishedButton to "Close" else set DialogPrompt to "Current Kindle for Mac key files: " & Kindlestring & " To add extra key files (.k4i), click the Add… button." set FinishedButton to "Close" end if set dialogresult to (display dialog DialogPrompt buttons {"Forget All", "Add…", FinishedButton} with title DialogTitle default button 2) if button returned of dialogresult is "Add…" then try set newFile to (choose file with prompt "Please select a Kindle for Mac/PC (.k4i) key file") as text -- copy to prefs AddKindleKey(newFile) on error message if message does not contain "cancel" then display dialog message end if end try else if button returned of dialogresult is "Forget All" then if Kindlestring is not "" then try set dialogresult to (display dialog "Are you sure you want to forget all extra Kindle for Mac key files?" buttons {"Cancel", "Forget"} default button 1 with title "DeDRM") end try if button returned of dialogresult is "Forget" then set KindleKeyList to {} end if end if else exit repeat end if end repeat end GetKindleKeyFiles on GetAndroidKeyFiles(DialogTitle) repeat set Androidstring to GetAndroidstring() if Androidstring is "" then set DialogPrompt to "To add Kindle for Android backup files (.ab), click the Add… button. " set FinishedButton to "Close" else set DialogPrompt to "Current Kindle for Android backup files: " & Androidstring & " To add extra backup files (.ab), click the Add… button." set FinishedButton to "Close" end if set dialogresult to (display dialog DialogPrompt buttons {"Forget All", "Add…", FinishedButton} with title DialogTitle default button 2) if button returned of dialogresult is "Add…" then try set newFile to (choose file with prompt "Please select a Kindle for Android backup (.ab) file") as text -- copy to prefs AddAndroidKey(newFile) on error message if message does not contain "cancel" then display dialog message end if end try else if button returned of dialogresult is "Forget All" then if Androidstring is not "" then try set dialogresult to (display dialog "Are you sure you want to forget all Kindle for Android backup files?" buttons {"Cancel", "Forget"} default button 1 with title "DeDRM") end try if button returned of dialogresult is "Forget" then set AndroidKeyList to {} end if end if else exit repeat end if end repeat end GetAndroidKeyFiles on GetAdeptKeys() set newKeyList to {} set userPrefsPath to path to preferences from user domain as string set preferencesFolderPath to userPrefsPath & prefsFolderName & ":" if not folderexists(preferencesFolderPath) then tell application "Finder" set preferencesFolder to make new folder at userPrefsPath with properties {name:prefsFolderName} end tell end if set newKeysFolderPath to preferencesFolderPath & newKeysFolderName & ":" if not folderexists(newKeysFolderPath) then tell application "Finder" set newKeysFolder to make new folder at preferencesFolderPath with properties {name:newKeysFolderName} end tell end if set scriptError to "" set shellresult to "" set success to true set shellcommand to python & (quoted form of AdobeKeyGenTool) & " " & quoted form of (POSIX path of (newKeysFolderPath)) set shellcommand to shellcommand & " > " & quotedtemppath() writetolog("shellcommand: " & shellcommand) cleartemp() try do shell script shellcommand on error scriptError set success to false end try set shellresult to readtemp() writetolog("shellresult: " & shellresult & " " & scriptError) --display dialog shellresult if success then --get all the files from newKeysFolder and add those that aren't duplicates tell application "Finder" to set fileList to (every file in folder newKeysFolderPath) repeat with this_item in fileList set userAdeptKeyPath to this_item as text set dupfile to false repeat with AdeptKey in AdeptKeyList set shellcommand to "cmp -s " & (quoted form of AdeptKey) & " " & quoted form of (POSIX path of (userAdeptKeyPath)) try --writetolog("shellcommand: " & shellcommand) do shell script shellcommand -- shellcommand will error on the files being different --writetolog("result: duplicate") set dupfile to true exit repeat on error scriptError --writetolog("result: different") -- do nothing end try end repeat if not dupfile then repeat with AdeptKey in newKeyList set shellcommand to "cmp -s " & (quoted form of AdeptKey) & " " & quoted form of (POSIX path of (userAdeptKeyPath)) try --writetolog("shellcommand: " & shellcommand) do shell script shellcommand -- shellcommand will error on the files being different --writetolog("result: duplicate") set dupfile to true exit repeat on error scriptError --writetolog("result: different") -- do nothing end try end repeat end if if not dupfile then -- move to the preferences folder, making sure the name's unique set newFilename to GetUniqueName(preferencesFolderPath, "AdobeKey.der") tell application "Finder" set name of file userAdeptKeyPath to newFilename set userAdeptKeyPath to newKeysFolderPath & newFilename move file userAdeptKeyPath to preferencesFolderPath with replacing end tell set newKeyList to newKeyList & (POSIX path of (preferencesFolderPath & newFilename)) end if end repeat end if -- delete new keys folder tell application "Finder" to move newKeysFolderPath to trash --display dialog newKeyList as text return newKeyList end GetAdeptKeys on GetKindleKeys() set newKeyList to {} set userPrefsPath to path to preferences from user domain as string set preferencesFolderPath to userPrefsPath & prefsFolderName & ":" if not folderexists(preferencesFolderPath) then tell application "Finder" set preferencesFolder to make new folder at userPrefsPath with properties {name:prefsFolderName} end tell end if set newKeysFolderPath to preferencesFolderPath & newKeysFolderName & ":" if not folderexists(newKeysFolderPath) then tell application "Finder" set newKeysFolder to make new folder at preferencesFolderPath with properties {name:newKeysFolderName} end tell end if set scriptError to "" set shellresult to "" set success to true set shellcommand to python & (quoted form of KindleKeyTool) & " " & quoted form of (POSIX path of (newKeysFolderPath)) set shellcommand to shellcommand & " > " & quotedtemppath() writetolog("shellcommand: " & shellcommand) cleartemp() try do shell script shellcommand on error scriptError set success to false end try set shellresult to readtemp() writetolog("shellresult: " & shellresult & " " & scriptError) --display dialog shellresult if success then --get all the files from newKeysFolder and add those that aren't duplicates tell application "Finder" to set fileList to (every file in folder newKeysFolderPath) repeat with this_item in fileList set userKeyPath to this_item as text set dupfile to false repeat with key in KindleKeyList set shellcommand to "cmp -s " & (quoted form of key) & " " & quoted form of (POSIX path of (userKeyPath)) try --writetolog("shellcommand: " & shellcommand) do shell script shellcommand -- shellcommand will error on the files being different --writetolog("result: duplicate") set dupfile to true exit repeat on error scriptError --writetolog("result: different") -- do nothing end try end repeat if not dupfile then repeat with key in newKeyList set shellcommand to "cmp -s " & (quoted form of key) & " " & quoted form of (POSIX path of (userKeyPath)) try --writetolog("shellcommand: " & shellcommand) do shell script shellcommand -- shellcommand will error on the files being different --writetolog("result: duplicate") set dupfile to true exit repeat on error scriptError --writetolog("result: different") -- do nothing end try end repeat end if if not dupfile then -- move to the preferences folder, making sure the name's unique set newFilename to GetUniqueName(preferencesFolderPath, "KindleKey.der") tell application "Finder" set name of file userKeyPath to newFilename set userKeyPath to newKeysFolderPath & newFilename move file userKeyPath to preferencesFolderPath with replacing end tell set newKeyList to newKeyList & (POSIX path of (preferencesFolderPath & newFilename)) end if end repeat end if -- delete new keys folder tell application "Finder" to move newKeysFolderPath to trash return newKeyList end GetKindleKeys on GetNookKeys() set newKeyList to {} set userPrefsPath to path to preferences from user domain as string set preferencesFolderPath to userPrefsPath & prefsFolderName & ":" if not folderexists(preferencesFolderPath) then tell application "Finder" set preferencesFolder to make new folder at userPrefsPath with properties {name:prefsFolderName} end tell end if set newKeysFolderPath to preferencesFolderPath & newKeysFolderName & ":" if not folderexists(newKeysFolderPath) then tell application "Finder" set newKeysFolder to make new folder at preferencesFolderPath with properties {name:newKeysFolderName} end tell end if set scriptError to "" set shellresult to "" set success to true set shellcommand to python & (quoted form of NookKeyTool) & " " & quoted form of (POSIX path of (newKeysFolderPath)) set shellcommand to shellcommand & " > " & quotedtemppath() writetolog("shellcommand: " & shellcommand) cleartemp() try do shell script shellcommand on error scriptError set success to false end try set shellresult to readtemp() writetolog("shellresult: " & shellresult & " " & scriptError) --display dialog shellresult if success then --get all the files from newKeysFolder and add those that aren't duplicates tell application "Finder" to set fileList to (every file in folder newKeysFolderPath) --display dialog (count of fileList) repeat with this_item in fileList set userKeyPath to this_item as text set dupfile to false repeat with key in bnKeys set keyfilepath to third item of key if length of keyfilepath > 0 then set shellcommand to "cmp -s " & (quoted form of keyfilepath) & " " & quoted form of (POSIX path of (userKeyPath)) try --writetolog("shellcommand: " & shellcommand) do shell script shellcommand -- shellcommand will error on the files being different --writetolog("result: duplicate") --display dialog "Deuplicate: " & userKeyPath & "//" & keyfilepath set dupfile to true exit repeat on error scriptError --writetolog("result: different") -- do nothing end try end if end repeat if not dupfile then repeat with key in newKeyList set keyfilepath to third item of key if length of keyfilepath > 0 then set shellcommand to "cmp -s " & (quoted form of keyfilepath) & " " & quoted form of (POSIX path of (userKeyPath)) try --writetolog("shellcommand: " & shellcommand) do shell script shellcommand -- shellcommand will error on the files being different --writetolog("result: duplicate") --display dialog "Deuplicate: " & userKeyPath & "//" & keyfilepath set dupfile to true exit repeat on error scriptError --writetolog("result: different") -- do nothing end try end if end repeat end if if not dupfile then -- move to the preferences folder, making sure the name's unique set newFilename to GetUniqueName(preferencesFolderPath, "Nook Study Key.b64") tell application "Finder" set name of file userKeyPath to newFilename set userKeyPath to newKeysFolderPath & newFilename move file userKeyPath to preferencesFolderPath with replacing end tell set newKeyList to newKeyList & {{newFilename, "", (POSIX path of (preferencesFolderPath & newFilename))}} end if end repeat end if -- delete new keys folder tell application "Finder" to move newKeysFolderPath to trash return newKeyList end GetNookKeys on encodeAsBase64(theFilePath) set posixfilepath to POSIX path of file theFilePath set base64path to POSIX path of file ((path to me as text) & "Contents:Resources:encodebase64.py") return do shell script python & quoted form of base64path & " " & quoted form of posixfilepath end encodeAsBase64 on AddAdeptKey(keyfile) set fileExtension to GetFileExtension(keyfile) if fileExtension is "der" then set newPrefsFile to CopyToPrefs(keyfile) set AdeptKeyList to AdeptKeyList & POSIX path of newPrefsFile else display dialog "Adobe Adept key files generated by adeptkey.py must have a .der extension." buttons {"OK"} default button 1 with title "DeDRM" with icon caution end if end AddAdeptKey on AddKindleKey(keyfile) set fileExtension to GetFileExtension(keyfile) if fileExtension is "k4i" then set newPrefsFile to CopyToPrefs(keyfile) set KindleKeyList to KindleKeyList & POSIX path of newPrefsFile else display dialog "Kindle key files generated by kindlekey.py must have a .k4i extension." buttons {"OK"} default button 1 with title "DeDRM" with icon caution end if end AddKindleKey on AddAndroidKey(keyfile) set fileExtension to GetFileExtension(keyfile) if fileExtension is "ab" or fileExtension is "db" or fileExtension is "xml" then set newPrefsFile to CopyToPrefs(keyfile) set AndroidKeyList to AndroidKeyList & POSIX path of newPrefsFile else display dialog "Android back files generated by use of \"adb backup com.amazon.kindle\" must have a .ab extension." buttons {"OK"} default button 1 with title "DeDRM" with icon caution end if end AddAndroidKey on AddbnKey(keyfile) set fileExtension to GetFileExtension(keyfile) if fileExtension is "b64" then set newPrefsFile to CopyToPrefs(keyfile) tell application "Finder" set keyfileName to (name of file newPrefsFile) as text end tell set fileName to GetFileNameOnly(keyfileName) set bnKeys to bnKeys & {{fileName, "", POSIX path of newPrefsFile}} else display dialog "Barnes & Noble key files must have a .b64 extension." buttons {"OK"} default button 1 with title "DeDRM" with icon caution end if end AddbnKey on GetUniqueName(folderPath, fileName) set newFilename to fileName if fileexists(folderPath & fileName) then -- first find new name that's unique to folder set namecount to 2 set fileExtension to GetFileExtension(fileName) set fileNameOnly to GetFileNameOnly(fileName) repeat set newFilename to fileNameOnly & " " & (namecount as text) & "." & fileExtension if not fileexists(folderPath & newFilename) then exit repeat end if set namecount to namecount + 1 end repeat end if return newFilename end GetUniqueName on CopyToPrefs(keyfilepath) tell application "Finder" set keyfileName to (name of file keyfilepath) as text end tell set keyfileApplepath to POSIX file (POSIX path of keyfilepath) set userPrefsPath to path to preferences from user domain as string set userTempPath to path to temporary items from user domain as string set preferencesFolderPath to userPrefsPath & prefsFolderName & ":" if not folderexists(preferencesFolderPath) then tell application "Finder" set preferencesFolder to make new folder at userPrefsPath with properties {name:prefsFolderName} end tell end if if fileexists(preferencesFolderPath & keyfileName) then -- copy to temporary items folder, rename, and then we can copy from there to the preferences folder -- first find new name set namecount to 2 set fileExtension to GetFileExtension(keyfileName) set keyfileNameOnly to GetFileNameOnly(keyfileName) repeat set newkeyfileName to keyfileNameOnly & " " & (namecount as text) & "." & fileExtension if not fileexists(preferencesFolderPath & newkeyfileName) and not fileexists(userTempPath & newkeyfileName) then exit repeat end if set namecount to namecount + 1 end repeat -- rename to new name set newkeyfilepath to userTempPath & newkeyfileName tell application "Finder" set tempfilepath to duplicate file keyfileApplepath to folder userTempPath with replacing set name of tempfilepath to newkeyfileName move file newkeyfilepath to preferencesFolderPath end tell set newFile to preferencesFolderPath & newkeyfileName else tell application "Finder" set newFile to duplicate file keyfileApplepath to folder preferencesFolderPath with replacing end tell end if return newFile as text end CopyToPrefs on ReadPrefs() -- read any existing keys from the preferences set preferencesFilePath to (path to preferences from user domain as string) & prefsFileName set PIDs to {} set UDIDs to {} set bnKeys to {} set KindleKeyList to {} set AndroidKeyList to {} set KindleSerialList to {} set AdeptKeyList to {} set outputFolder to (path to desktop as string) set logFilePath to (path to desktop as string) & "DeDRM.log" set tempfilepath to (path to preferences from user domain as string) & prefsFolderName & ":" & tempFileName --set tempfilepath to (path to temporary items as string)& "DeDRM_temp.log" if fileexists(POSIX path of file preferencesFilePath) then tell application "System Events" try set PIDs to value of property list item "PIDs" of property list file preferencesFilePath end try try set UDIDs to value of property list item "UDIDs" of property list file preferencesFilePath end try try set KindleSerialList to value of property list item "KindleSerials" of property list file preferencesFilePath end try try set bnKeys to value of property list item "bnKeys" of property list file preferencesFilePath end try try set AdeptKeyList to value of property list item "AdeptKeys" of property list file preferencesFilePath end try try set AndroidKeyList to value of property list item "AndroidKeys" of property list file preferencesFilePath end try try set KindleKeyList to value of property list item "KindleKeys" of property list file preferencesFilePath end try try set outputFolder to value of property list item "OutputFolder" of property list file preferencesFilePath end try end tell end if set newList to {} repeat with i from 1 to count AndroidKeyList if fileexists(AndroidKeyList's item i) then set newList's end to AndroidKeyList's item i end repeat set AndroidKeyList to newList set newList to {} repeat with i from 1 to count AdeptKeyList if fileexists(AdeptKeyList's item i) then set newList's end to AdeptKeyList's item i end repeat set AdeptKeyList to newList set newList to {} repeat with i from 1 to count KindleKeyList if fileexists(KindleKeyList's item i) then set newList's end to KindleKeyList's item i end repeat set KindleKeyList to newList set newList to {} repeat with i from 1 to count bnKeys if (item 3 of bnKeys's item i) is "" or fileexists(item 3 of bnKeys's item i) then set newList's end to bnKeys's item i end repeat set bnKeys to newList end ReadPrefs on CreatePlist(filePath) set theEmptyPListData to " " set thePListFile to open for access filePath with write permission set eof of thePListFile to 0 write theEmptyPListData to thePListFile starting at eof close access thePListFile end CreatePlist on WritePrefsOld() -- write keys to the preferences from older AppleScript set preferencesFilePath to (path to preferences from user domain as string) & prefsFileName CreatePlist(preferencesFilePath) tell application "System Events" tell contents of property list file preferencesFilePath set value to ({|PIDs|:PIDs} & {|UDIDs|:UDIDs} & {|AndroidKeys|:AndroidKeyList} & {|KindleSerials|:KindleSerialList} & {|KindleKeys|:KindleKeyList} & {|bnKeys|:bnKeys} & {|AdeptKeys|:AdeptKeyList} & {|OutputFolder|:outputFolder}) end tell end tell end WritePrefsOld on WritePrefs() if oldAppleScript then WritePrefsOld() else -- write keys to the preferences for AppleScript 2.0 and later set preferencesFilePath to (path to preferences from user domain as string) & prefsFileName -- we still need to create an empty file in Yosemite, apparently CreatePlist(preferencesFilePath) tell application "System Events" set the base_dict to make new property list item with properties {kind:record} set myPrefs to make new property list file with properties {contents:base_dict, name:preferencesFilePath} make new property list item at end of property list items of contents of myPrefs with properties {kind:list, name:"PIDs", value:PIDs} make new property list item at end of property list items of contents of myPrefs with properties {kind:list, name:"UDIDs", value:UDIDs} make new property list item at end of property list items of contents of myPrefs with properties {kind:list, name:"AndroidKeys", value:AndroidKeyList} make new property list item at end of property list items of contents of myPrefs with properties {kind:list, name:"KindleSerials", value:KindleSerialList} make new property list item at end of property list items of contents of myPrefs with properties {kind:list, name:"KindleKeys", value:KindleKeyList} make new property list item at end of property list items of contents of myPrefs with properties {kind:list, name:"bnKeys", value:bnKeys} make new property list item at end of property list items of contents of myPrefs with properties {kind:list, name:"AdeptKeys", value:AdeptKeyList} make new property list item at end of property list items of contents of myPrefs with properties {kind:string, name:"OutputFolder", value:outputFolder} end tell end if end WritePrefs on InitProgress(maxcount) try if totalebooks > 1 then launch application ((path to resource "DeDRM Progress.app") as string) tell application ((path to resource "DeDRM Progress.app") as string) activate set contents of «class texF» "maintext" of window "main" to "Decrypting " & maxcount & " ebooks." set contents of «class texF» "subtext" of window "main" to "Decrypting book 0: " set «class maxV» of «class proI» "bar" of window "main" to maxcount set «class conT» of «class proI» "bar" of window "main" to 0 end tell end if end try end InitProgress on ProcessItems(some_items) try set ErrorList to "" set WarningList to "" set CompletedList to "" set ErrorCount to 0 set WarningCount to 0 set CompletedCount to 0 set totalebooks to 0 set completedebooks to 0 repeat with this_item in some_items if (folder of (info for this_item) is true) then tell application "Finder" to set totalebooks to totalebooks + (count of (every file in entire contents of folder this_item whose name extension is in handledExtensions)) else countfile(this_item as text) end if end repeat if totalebooks is 1 then set TotalText to "There is 1 ebook to be decrypted." else set TotalText to "There are " & totalebooks & " ebooks to be decrypted." end if writetolog(TotalText) InitProgress(totalebooks) repeat with this_item in some_items if totalebooks > 1 and not ProgressActive() then exit repeat end if if (folder of (info for this_item) is true) then handlefolder(this_item as text) else handlefile(this_item as text) end if end repeat endProgress() on error errmsg number errnum set errorReport to "An unexpected error occurred. Please report on Apprentice Alf's blog. Error Message: " & errmsg & " Error Number: " & errnum writetolog(errorReport) display notification errorReport with title "DeDRM" subtitle "Unexpected Error" --display dialog errorReport with title "DeDRM" buttons {"Bother"} default button 1 with icon stop end try if WarningList is not "" then set WarningText to "" if WarningCount is 1 then set WarningText to "There was a warning with 1 ebook:" else set WarningText to "There were warnings with " & WarningCount & " ebooks:" end if writetolog(WarningText) writetolog(WarningList) --set dialogresult to display dialog WarningText & " --" & WarningList buttons {"OK"} with title "DeDRM" default button 1 display notification WarningList with title "DeDRM" subtitle WarningText end if if ErrorList is not "" then set ErrorText to "" if ErrorCount is 1 then set ErrorText to "There was an error with 1 ebook:" else set ErrorText to "There were errors with " & ErrorCount & " ebooks:" end if writetolog(ErrorText) writetolog(ErrorList) --set dialogresult to display dialog ErrorText & " --" & ErrorList buttons {"Bother"} with title "DeDRM" default button 1 display notification ErrorList with title "DeDRM" subtitle ErrorText end if if CompletedCount > 0 then set CompletedText to "" if CompletedCount is 1 then set CompletedText to "Successfully de-drmed 1 ebook:" else set CompletedText to "Successfully de-drmed " & CompletedCount & " ebooks:" end if writetolog(CompletedText) writetolog(CompletedList) --set dialogresult to display dialog CompletedText & " --" & CompletedList buttons {"Thanks"} with title "DeDRM" default button 1 display notification CompletedList with title "DeDRM" subtitle CompletedText end if return CompletedCount + WarningCount + ErrorCount end ProcessItems on ProgressActive() tell application "System Events" to set progactive to count (every process whose name is "DeDRM Progress") if progactive > 0 then return true return false end ProgressActive on IncProgress(booktitle, doingcount) try if ProgressActive() then tell application ((path to resource "DeDRM Progress.app") as string) set contents of «class texF» "subtext" of window "main" to "Decrypting book " & doingcount & ": " & booktitle set «class conT» of «class proI» "bar" of window "main" to doingcount end tell end if end try end IncProgress on endProgress() try if ProgressActive() then tell application ((path to resource "DeDRM Progress.app") as string) to quit end try end endProgress on run if GetTools() then ReadPrefs() clearlog() repeat set dialogresult to display dialog "To remove DRM from an ebook, click ‘Select Ebook…’ To configure this software, click ‘Configure…’ When the DeDRM application is not running, you can also remove DRM from ebooks by dropping them onto the DeDRM application icon in the Finder. For details of the ebook formats which this application can handle, see the ReadMe file or visit http://apprenticealf.wordpress.com/ Please only use this application on your own books. Authors, publishers and ebook stores need to make money to produce more ebooks. Don't cheat them. This application is by Apprentice Alf and Apprentice Harper based on an AppleScript script by Paul Durrant and uses python scripts produced by Apprentice Alf, CMBDTC, The Dark Reverser, DiapDealer, i♥cabbages, some_updates, Apprentice Harper and others. The AppleScript part of this application is free software released into the public domain, see http://unlicense.org/ The application icon is adapted from the Authors Against DRM logo at http://readersbillofrights.info/AAD and is under the Creative Commons Attribution-ShareAlike licence. The included Python scripts are all free to use, but have a variety of licences. See the individual files for details. " with title "DeDRM by Apprentice Alf" buttons {"Quit", "Select Ebook… ", "Configure… "} default button 1 with icon note if button returned of dialogresult is "Quit" then exit repeat if button returned of dialogresult is "Select Ebook… " then set DRMebook to "" try set DRMebook to {choose file with prompt "Please select a DRMed ebook"} end try if DRMebook is not "" then if ProcessItems(DRMebook) is 0 then display dialog "That was a file that this script cannot process." with title "DeDRM" buttons {"OK"} default button 1 with icon note end if WritePrefs() end if else set choice to true repeat while choice is not false set choice to choose from list {"eInk Kindle eBooks", "Barnes and Noble (nook) ebooks", "Mobipocket eBooks", "eReader ebooks", "Adobe Digital Editions ebooks", "Kindle for Mac ebooks", "Output Folder"} with prompt "Choose a Configuration Option:" with title "DeDRM Configuration" OK button name "Configure..." cancel button name "Finished" if choice contains "Barnes and Noble (nook) ebooks" then GetBNKeys("DeDRM Configure for Barnes and Noble (nook)") else if choice contains "eInk Kindle eBooks" then GetSerials("DeDRM Configure for eInk Kindle") else if choice contains "Mobipocket eBooks" then GetPIDs("DeDRM Configure for Mobipocket") else if choice contains "eReader ebooks" then GeteReaderKeys("DeDRM Configure for eReader") else if choice contains "Kindle for Mac ebooks" then GetKindleKeyFiles("DeDRM Configure Kindle for Mac") else if choice contains "Kindle for Android ebooks" then GetAndroidKeyFiles("DeDRM Configure Kindle for Android") else if choice contains "Adobe Digital Editions ebooks" then GetAdeptKeyFiles("DeDRM Configure Adobe Digital Editions") else if choice contains "Output Folder" then GetOutputFolder("DeDRM Configure Output Folder") end if end repeat WritePrefs() end if end repeat end if end run on open some_items --display dialog (some_items as text) if GetTools() then ReadPrefs() clearlog() if ProcessItems(some_items) is 0 then display notification "No ebooks found in the dropped items. DeDRM can handle Mobipocket, Kindle, eReader, Adobe ePub, Barnes & Noble ePub and Adobe PDF. Please try again." with title "DeDRM" end if WritePrefs() delay 1 --> allow time for any notifications to trigger end if end open