BVB Source Codes

DeDRM_tools Show mobidedrm.py Source code

Return Download DeDRM_tools: download mobidedrm.py Source code - Download DeDRM_tools Source code - Type:.py
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3.  
  4. # mobidedrm.py, version 0.38
  5. # Copyright 漏 2008 The Dark Reverser
  6. #
  7. # Modified 2008鈥?012 by some_updates, DiapDealer and Apprentice Alf
  8.  
  9. # This is a python script. You need a Python interpreter to run it.
  10. # For example, ActiveState Python, which exists for windows.
  11. #
  12. # Changelog
  13. #  0.01 - Initial version
  14. #  0.02 - Huffdic compressed books were not properly decrypted
  15. #  0.03 - Wasn't checking MOBI header length
  16. #  0.04 - Wasn't sanity checking size of data record
  17. #  0.05 - It seems that the extra data flags take two bytes not four
  18. #  0.06 - And that low bit does mean something after all :-)
  19. #  0.07 - The extra data flags aren't present in MOBI header < 0xE8 in size
  20. #  0.08 - ...and also not in Mobi header version < 6
  21. #  0.09 - ...but they are there with Mobi header version 6, header size 0xE4!
  22. #  0.10 - Outputs unencrypted files as-is, so that when run as a Calibre
  23. #         import filter it works when importing unencrypted files.
  24. #         Also now handles encrypted files that don't need a specific PID.
  25. #  0.11 - use autoflushed stdout and proper return values
  26. #  0.12 - Fix for problems with metadata import as Calibre plugin, report errors
  27. #  0.13 - Formatting fixes: retabbed file, removed trailing whitespace
  28. #         and extra blank lines, converted CR/LF pairs at ends of each line,
  29. #         and other cosmetic fixes.
  30. #  0.14 - Working out when the extra data flags are present has been problematic
  31. #         Versions 7 through 9 have tried to tweak the conditions, but have been
  32. #         only partially successful. Closer examination of lots of sample
  33. #         files reveals that a confusion has arisen because trailing data entries
  34. #         are not encrypted, but it turns out that the multibyte entries
  35. #         in utf8 file are encrypted. (Although neither kind gets compressed.)
  36. #         This knowledge leads to a simplification of the test for the
  37. #         trailing data byte flags - version 5 and higher AND header size >= 0xE4.
  38. #  0.15 - Now outputs 'heartbeat', and is also quicker for long files.
  39. #  0.16 - And reverts to 'done' not 'done.' at the end for unswindle compatibility.
  40. #  0.17 - added modifications to support its use as an imported python module
  41. #         both inside calibre and also in other places (ie K4DeDRM tools)
  42. #  0.17a- disabled the standalone plugin feature since a plugin can not import
  43. #         a plugin
  44. #  0.18 - It seems that multibyte entries aren't encrypted in a v7 file...
  45. #         Removed the disabled Calibre plug-in code
  46. #         Permit use of 8-digit PIDs
  47. #  0.19 - It seems that multibyte entries aren't encrypted in a v6 file either.
  48. #  0.20 - Correction: It seems that multibyte entries are encrypted in a v6 file.
  49. #  0.21 - Added support for multiple pids
  50. #  0.22 - revised structure to hold MobiBook as a class to allow an extended interface
  51. #  0.23 - fixed problem with older files with no EXTH section
  52. #  0.24 - add support for type 1 encryption and 'TEXtREAd' books as well
  53. #  0.25 - Fixed support for 'BOOKMOBI' type 1 encryption
  54. #  0.26 - Now enables Text-To-Speech flag and sets clipping limit to 100%
  55. #  0.27 - Correct pid metadata token generation to match that used by skindle (Thank You Bart!)
  56. #  0.28 - slight additional changes to metadata token generation (None -> '')
  57. #  0.29 - It seems that the ideas about when multibyte trailing characters were
  58. #         included in the encryption were wrong. They are for DOC compressed
  59. #         files, but they are not for HUFF/CDIC compress files!
  60. #  0.30 - Modified interface slightly to work better with new calibre plugin style
  61. #  0.31 - The multibyte encrytion info is true for version 7 files too.
  62. #  0.32 - Added support for "Print Replica" Kindle ebooks
  63. #  0.33 - Performance improvements for large files (concatenation)
  64. #  0.34 - Performance improvements in decryption (libalfcrypto)
  65. #  0.35 - add interface to get mobi_version
  66. #  0.36 - fixed problem with TEXtREAd and getBookTitle interface
  67. #  0.37 - Fixed double announcement for stand-alone operation
  68. #  0.38 - Unicode used wherever possible, cope with absent alfcrypto
  69. #  0.39 - Fixed problem with TEXtREAd and getBookType interface
  70. #  0.40 - moved unicode_argv call inside main for Windows DeDRM compatibility
  71. #  0.41 - Fixed potential unicode problem in command line calls
  72.  
  73.  
  74. __version__ = u"0.41"
  75.  
  76. import sys
  77. import os
  78. import struct
  79. import binascii
  80. try:
  81.     from alfcrypto import Pukall_Cipher
  82. except:
  83.     print u"AlfCrypto not found. Using python PC1 implementation."
  84.  
  85. # Wrap a stream so that output gets flushed immediately
  86. # and also make sure that any unicode strings get
  87. # encoded using "replace" before writing them.
  88. class SafeUnbuffered:
  89.     def __init__(self, stream):
  90.         self.stream = stream
  91.         self.encoding = stream.encoding
  92.         if self.encoding == None:
  93.             self.encoding = "utf-8"
  94.     def write(self, data):
  95.         if isinstance(data,unicode):
  96.             data = data.encode(self.encoding,"replace")
  97.         self.stream.write(data)
  98.         self.stream.flush()
  99.     def __getattr__(self, attr):
  100.         return getattr(self.stream, attr)
  101.  
  102. iswindows = sys.platform.startswith('win')
  103. isosx = sys.platform.startswith('darwin')
  104.  
  105. def unicode_argv():
  106.     if iswindows:
  107.         # Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
  108.         # strings.
  109.  
  110.         # Versions 2.x of Python don't support Unicode in sys.argv on
  111.         # Windows, with the underlying Windows API instead replacing multi-byte
  112.         # characters with '?'.
  113.  
  114.  
  115.         from ctypes import POINTER, byref, cdll, c_int, windll
  116.         from ctypes.wintypes import LPCWSTR, LPWSTR
  117.  
  118.         GetCommandLineW = cdll.kernel32.GetCommandLineW
  119.         GetCommandLineW.argtypes = []
  120.         GetCommandLineW.restype = LPCWSTR
  121.  
  122.         CommandLineToArgvW = windll.shell32.CommandLineToArgvW
  123.         CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
  124.         CommandLineToArgvW.restype = POINTER(LPWSTR)
  125.  
  126.         cmd = GetCommandLineW()
  127.         argc = c_int(0)
  128.         argv = CommandLineToArgvW(cmd, byref(argc))
  129.         if argc.value > 0:
  130.             # Remove Python executable and commands if present
  131.             start = argc.value - len(sys.argv)
  132.             return [argv[i] for i in
  133.                     xrange(start, argc.value)]
  134.         # if we don't have any arguments at all, just pass back script name
  135.         # this should never happen
  136.         return [u"mobidedrm.py"]
  137.     else:
  138.         argvencoding = sys.stdin.encoding
  139.         if argvencoding == None:
  140.             argvencoding = 'utf-8'
  141.         return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
  142.  
  143.  
  144. class DrmException(Exception):
  145.     pass
  146.  
  147.  
  148. #
  149. # MobiBook Utility Routines
  150. #
  151.  
  152. # Implementation of Pukall Cipher 1
  153. def PC1(key, src, decryption=True):
  154.     # if we can get it from alfcrypto, use that
  155.     try:
  156.         return Pukall_Cipher().PC1(key,src,decryption)
  157.     except NameError:
  158.         pass
  159.     except TypeError:
  160.         pass
  161.  
  162.     # use slow python version, since Pukall_Cipher didn't load
  163.     sum1 = 0;
  164.     sum2 = 0;
  165.     keyXorVal = 0;
  166.     if len(key)!=16:
  167.          DrmException (u"PC1: Bad key length")
  168.     wkey = []
  169.     for i in xrange(8):
  170.         wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1]))
  171.     dst = ""
  172.     for i in xrange(len(src)):
  173.         temp1 = 0;
  174.         byteXorVal = 0;
  175.         for j in xrange(8):
  176.             temp1 ^= wkey[j]
  177.             sum2  = (sum2+j)*20021 + sum1
  178.             sum1  = (temp1*346)&0xFFFF
  179.             sum2  = (sum2+sum1)&0xFFFF
  180.             temp1 = (temp1*20021+1)&0xFFFF
  181.             byteXorVal ^= temp1 ^ sum2
  182.         curByte = ord(src[i])
  183.         if not decryption:
  184.             keyXorVal = curByte * 257;
  185.         curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
  186.         if decryption:
  187.             keyXorVal = curByte * 257;
  188.         for j in xrange(8):
  189.             wkey[j] ^= keyXorVal;
  190.         dst+=chr(curByte)
  191.     return dst
  192.  
  193. def checksumPid(s):
  194.     letters = 'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
  195.     crc = (~binascii.crc32(s,-1))&0xFFFFFFFF
  196.     crc = crc ^ (crc >> 16)
  197.     res = s
  198.     l = len(letters)
  199.     for i in (0,1):
  200.         b = crc & 0xff
  201.         pos = (b // l) ^ (b % l)
  202.         res += letters[pos%l]
  203.         crc >>= 8
  204.     return res
  205.  
  206. def getSizeOfTrailingDataEntries(ptr, size, flags):
  207.     def getSizeOfTrailingDataEntry(ptr, size):
  208.         bitpos, result = 0, 0
  209.         if size <= 0:
  210.             return result
  211.         while True:
  212.             v = ord(ptr[size-1])
  213.             result |= (v & 0x7F) << bitpos
  214.             bitpos += 7
  215.             size -= 1
  216.             if (v & 0x80) != 0 or (bitpos >= 28) or (size == 0):
  217.                 return result
  218.     num = 0
  219.     testflags = flags >> 1
  220.     while testflags:
  221.         if testflags & 1:
  222.             num += getSizeOfTrailingDataEntry(ptr, size - num)
  223.         testflags >>= 1
  224.     # Check the low bit to see if there's multibyte data present.
  225.     # if multibyte data is included in the encryped data, we'll
  226.     # have already cleared this flag.
  227.     if flags & 1:
  228.         num += (ord(ptr[size - num - 1]) & 0x3) + 1
  229.     return num
  230.  
  231.  
  232.  
  233. class MobiBook:
  234.     def loadSection(self, section):
  235.         if (section + 1 == self.num_sections):
  236.             endoff = len(self.data_file)
  237.         else:
  238.             endoff = self.sections[section + 1][0]
  239.         off = self.sections[section][0]
  240.         return self.data_file[off:endoff]
  241.  
  242.     def cleanup(self):
  243.         # to match function in Topaz book
  244.         pass
  245.  
  246.     def __init__(self, infile):
  247.         print u"MobiDeDrm v{0:s}.\nCopyright 漏 2008-2012 The Dark Reverser et al.".format(__version__)
  248.  
  249.         try:
  250.             from alfcrypto import Pukall_Cipher
  251.         except:
  252.             print u"AlfCrypto not found. Using python PC1 implementation."
  253.  
  254.         # initial sanity check on file
  255.         self.data_file = file(infile, 'rb').read()
  256.         self.mobi_data = ''
  257.         self.header = self.data_file[0:78]
  258.         if self.header[0x3C:0x3C+8] != 'BOOKMOBI' and self.header[0x3C:0x3C+8] != 'TEXtREAd':
  259.             raise DrmException(u"Invalid file format")
  260.         self.magic = self.header[0x3C:0x3C+8]
  261.         self.crypto_type = -1
  262.  
  263.         # build up section offset and flag info
  264.         self.num_sections, = struct.unpack('>H', self.header[76:78])
  265.         self.sections = []
  266.         for i in xrange(self.num_sections):
  267.             offset, a1,a2,a3,a4 = struct.unpack('>LBBBB', self.data_file[78+i*8:78+i*8+8])
  268.             flags, val = a1, a2<<16|a3<<8|a4
  269.             self.sections.append( (offset, flags, val) )
  270.  
  271.         # parse information from section 0
  272.         self.sect = self.loadSection(0)
  273.         self.records, = struct.unpack('>H', self.sect[0x8:0x8+2])
  274.         self.compression, = struct.unpack('>H', self.sect[0x0:0x0+2])
  275.  
  276.         # det default values before PalmDoc test
  277.         self.print_replica = False
  278.         self.extra_data_flags = 0
  279.         self.meta_array = {}
  280.         self.mobi_length = 0
  281.         self.mobi_codepage = 1252
  282.         self.mobi_version = -1
  283.  
  284.         if self.magic == 'TEXtREAd':
  285.             print u"PalmDoc format book detected."
  286.             return
  287.  
  288.         self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18])
  289.         self.mobi_codepage, = struct.unpack('>L',self.sect[0x1c:0x20])
  290.         self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C])
  291.         print u"MOBI header version {0:d}, header length {1:d}".format(self.mobi_version, self.mobi_length)
  292.         if (self.mobi_length >= 0xE4) and (self.mobi_version >= 5):
  293.             self.extra_data_flags, = struct.unpack('>H', self.sect[0xF2:0xF4])
  294.             print u"Extra Data Flags: {0:d}".format(self.extra_data_flags)
  295.         if (self.compression != 17480):
  296.             # multibyte utf8 data is included in the encryption for PalmDoc compression
  297.             # so clear that byte so that we leave it to be decrypted.
  298.             self.extra_data_flags &= 0xFFFE
  299.  
  300.         # if exth region exists parse it for metadata array
  301.         try:
  302.             exth_flag, = struct.unpack('>L', self.sect[0x80:0x84])
  303.             exth = ''
  304.             if exth_flag & 0x40:
  305.                 exth = self.sect[16 + self.mobi_length:]
  306.             if (len(exth) >= 12) and (exth[:4] == 'EXTH'):
  307.                 nitems, = struct.unpack('>I', exth[8:12])
  308.                 pos = 12
  309.                 for i in xrange(nitems):
  310.                     type, size = struct.unpack('>II', exth[pos: pos + 8])
  311.                     content = exth[pos + 8: pos + size]
  312.                     self.meta_array[type] = content
  313.                     # reset the text to speech flag and clipping limit, if present
  314.                     if type == 401 and size == 9:
  315.                         # set clipping limit to 100%
  316.                         self.patchSection(0, '\144', 16 + self.mobi_length + pos + 8)
  317.                     elif type == 404 and size == 9:
  318.                         # make sure text to speech is enabled
  319.                         self.patchSection(0, '\0', 16 + self.mobi_length + pos + 8)
  320.                     # print type, size, content, content.encode('hex')
  321.                     pos += size
  322.         except:
  323.             pass
  324.  
  325.     def getBookTitle(self):
  326.         codec_map = {
  327.             1252 : 'windows-1252',
  328.             65001 : 'utf-8',
  329.         }
  330.         title = ''
  331.         codec = 'windows-1252'
  332.         if self.magic == 'BOOKMOBI':
  333.             if 503 in self.meta_array:
  334.                 title = self.meta_array[503]
  335.             else:
  336.                 toff, tlen = struct.unpack('>II', self.sect[0x54:0x5c])
  337.                 tend = toff + tlen
  338.                 title = self.sect[toff:tend]
  339.             if self.mobi_codepage in codec_map.keys():
  340.                 codec = codec_map[self.mobi_codepage]
  341.         if title == '':
  342.             title = self.header[:32]
  343.             title = title.split('\0')[0]
  344.         return unicode(title, codec)
  345.  
  346.     def getPIDMetaInfo(self):
  347.         rec209 = ''
  348.         token = ''
  349.         if 209 in self.meta_array:
  350.             rec209 = self.meta_array[209]
  351.             data = rec209
  352.             # The 209 data comes in five byte groups. Interpret the last four bytes
  353.             # of each group as a big endian unsigned integer to get a key value
  354.             # if that key exists in the meta_array, append its contents to the token
  355.             for i in xrange(0,len(data),5):
  356.                 val,  = struct.unpack('>I',data[i+1:i+5])
  357.                 sval = self.meta_array.get(val,'')
  358.                 token += sval
  359.         return rec209, token
  360.  
  361.     def patch(self, off, new):
  362.         self.data_file = self.data_file[:off] + new + self.data_file[off+len(new):]
  363.  
  364.     def patchSection(self, section, new, in_off = 0):
  365.         if (section + 1 == self.num_sections):
  366.             endoff = len(self.data_file)
  367.         else:
  368.             endoff = self.sections[section + 1][0]
  369.         off = self.sections[section][0]
  370.         assert off + in_off + len(new) <= endoff
  371.         self.patch(off + in_off, new)
  372.  
  373.     def parseDRM(self, data, count, pidlist):
  374.         found_key = None
  375.         keyvec1 = '\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96'
  376.         for pid in pidlist:
  377.             bigpid = pid.ljust(16,'\0')
  378.             temp_key = PC1(keyvec1, bigpid, False)
  379.             temp_key_sum = sum(map(ord,temp_key)) & 0xff
  380.             found_key = None
  381.             for i in xrange(count):
  382.                 verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
  383.                 if cksum == temp_key_sum:
  384.                     cookie = PC1(temp_key, cookie)
  385.                     ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie)
  386.                     if verification == ver and (flags & 0x1F) == 1:
  387.                         found_key = finalkey
  388.                         break
  389.             if found_key != None:
  390.                 break
  391.         if not found_key:
  392.             # Then try the default encoding that doesn't require a PID
  393.             pid = '00000000'
  394.             temp_key = keyvec1
  395.             temp_key_sum = sum(map(ord,temp_key)) & 0xff
  396.             for i in xrange(count):
  397.                 verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
  398.                 if cksum == temp_key_sum:
  399.                     cookie = PC1(temp_key, cookie)
  400.                     ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie)
  401.                     if verification == ver:
  402.                         found_key = finalkey
  403.                         break
  404.         return [found_key,pid]
  405.  
  406.     def getFile(self, outpath):
  407.         file(outpath,'wb').write(self.mobi_data)
  408.  
  409.     def getBookType(self):
  410.         if self.print_replica:
  411.             return u"Print Replica"
  412.         if self.mobi_version >= 8:
  413.             return u"Kindle Format 8"
  414.         if self.mobi_version >= 0:
  415.             return u"Mobipocket {0:d}".format(self.mobi_version)
  416.         return u"PalmDoc"
  417.  
  418.     def getBookExtension(self):
  419.         if self.print_replica:
  420.             return u".azw4"
  421.         if self.mobi_version >= 8:
  422.             return u".azw3"
  423.         return u".mobi"
  424.  
  425.     def processBook(self, pidlist):
  426.         crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2])
  427.         print u"Crypto Type is: {0:d}".format(crypto_type)
  428.         self.crypto_type = crypto_type
  429.         if crypto_type == 0:
  430.             print u"This book is not encrypted."
  431.             # we must still check for Print Replica
  432.             self.print_replica = (self.loadSection(1)[0:4] == '%MOP')
  433.             self.mobi_data = self.data_file
  434.             return
  435.         if crypto_type != 2 and crypto_type != 1:
  436.             raise DrmException(u"Cannot decode unknown Mobipocket encryption type {0:d}".format(crypto_type))
  437.         if 406 in self.meta_array:
  438.             data406 = self.meta_array[406]
  439.             val406, = struct.unpack('>Q',data406)
  440.             if val406 != 0:
  441.                 raise DrmException(u"Cannot decode library or rented ebooks.")
  442.  
  443.         goodpids = []
  444.         for pid in pidlist:
  445.             if len(pid)==10:
  446.                 if checksumPid(pid[0:-2]) != pid:
  447.                     print u"Warning: PID {0} has incorrect checksum, should have been {1}".format(pid,checksumPid(pid[0:-2]))
  448.                 goodpids.append(pid[0:-2])
  449.             elif len(pid)==8:
  450.                 goodpids.append(pid)
  451.             else:
  452.                 print u"Warning: PID {0} has wrong number of digits".format(pid)
  453.  
  454.         if self.crypto_type == 1:
  455.             t1_keyvec = 'QDCVEPMU675RUBSZ'
  456.             if self.magic == 'TEXtREAd':
  457.                 bookkey_data = self.sect[0x0E:0x0E+16]
  458.             elif self.mobi_version < 0:
  459.                 bookkey_data = self.sect[0x90:0x90+16]
  460.             else:
  461.                 bookkey_data = self.sect[self.mobi_length+16:self.mobi_length+32]
  462.             pid = '00000000'
  463.             found_key = PC1(t1_keyvec, bookkey_data)
  464.         else :
  465.             # calculate the keys
  466.             drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', self.sect[0xA8:0xA8+16])
  467.             if drm_count == 0:
  468.                 raise DrmException(u"Encryption not initialised. Must be opened with Mobipocket Reader first.")
  469.             found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids)
  470.             if not found_key:
  471.                 raise DrmException(u"No key found in {0:d} keys tried.".format(len(goodpids)))
  472.             # kill the drm keys
  473.             self.patchSection(0, '\0' * drm_size, drm_ptr)
  474.             # kill the drm pointers
  475.             self.patchSection(0, '\xff' * 4 + '\0' * 12, 0xA8)
  476.  
  477.         if pid=='00000000':
  478.             print u"File has default encryption, no specific key needed."
  479.         else:
  480.             print u"File is encoded with PID {0}.".format(checksumPid(pid))
  481.  
  482.         # clear the crypto type
  483.         self.patchSection(0, "\0" * 2, 0xC)
  484.  
  485.         # decrypt sections
  486.         print u"Decrypting. Please wait . . .",
  487.         mobidataList = []
  488.         mobidataList.append(self.data_file[:self.sections[1][0]])
  489.         for i in xrange(1, self.records+1):
  490.             data = self.loadSection(i)
  491.             extra_size = getSizeOfTrailingDataEntries(data, len(data), self.extra_data_flags)
  492.             if i%100 == 0:
  493.                 print u".",
  494.             # print "record %d, extra_size %d" %(i,extra_size)
  495.             decoded_data = PC1(found_key, data[0:len(data) - extra_size])
  496.             if i==1:
  497.                 self.print_replica = (decoded_data[0:4] == '%MOP')
  498.             mobidataList.append(decoded_data)
  499.             if extra_size > 0:
  500.                 mobidataList.append(data[-extra_size:])
  501.         if self.num_sections > self.records+1:
  502.             mobidataList.append(self.data_file[self.sections[self.records+1][0]:])
  503.         self.mobi_data = "".join(mobidataList)
  504.         print u"done"
  505.         return
  506.  
  507. def getUnencryptedBook(infile,pidlist):
  508.     if not os.path.isfile(infile):
  509.         raise DrmException(u"Input File Not Found.")
  510.     book = MobiBook(infile)
  511.     book.processBook(pidlist)
  512.     return book.mobi_data
  513.  
  514.  
  515. def cli_main():
  516.     argv=unicode_argv()
  517.     progname = os.path.basename(argv[0])
  518.     if len(argv)<3 or len(argv)>4:
  519.         print u"MobiDeDrm v{0}.\nCopyright 漏 2008-2012 The Dark Reverser et al.".format(__version__)
  520.         print u"Removes protection from Kindle/Mobipocket, Kindle/KF8 and Kindle/Print Replica ebooks"
  521.         print u"Usage:"
  522.         print u"    {0} <infile> <outfile> [<Comma separated list of PIDs to try>]".format(progname)
  523.         return 1
  524.     else:
  525.         infile = argv[1]
  526.         outfile = argv[2]
  527.         if len(argv) is 4:
  528.             pidlist = argv[3].split(',')
  529.         else:
  530.             pidlist = []
  531.         try:
  532.             stripped_file = getUnencryptedBook(infile, pidlist)
  533.             file(outfile, 'wb').write(stripped_file)
  534.         except DrmException, e:
  535.             print u"MobiDeDRM v{0} Error: {1:s}".format(__version__,e.args[0])
  536.             return 1
  537.     return 0
  538.  
  539.  
  540. if __name__ == '__main__':
  541.     sys.stdout=SafeUnbuffered(sys.stdout)
  542.     sys.stderr=SafeUnbuffered(sys.stderr)
  543.     sys.exit(cli_main())
  544.  
downloadmobidedrm.py Source code - Download DeDRM_tools Source code
Related Source Codes/Software:
kcp - KCP - A Fast and Reliable ARQ Protocol 2017-04-08
cloc - cloc counts blank lines, comment lines, and physic... 2017-04-08
nvidia-docker - Build and run Docker containers leveraging NVIDIA ... 2017-04-08
quickstart - Angular 2 QuickStart - source from the documentati... 2017-04-08
jQuery-Autocomplete - Ajax Autocomplete for jQuery allows you to easily ... 2017-04-08
fuckitjs - The Original Javascript Error Steamroller 2017-04-09
eslint-plugin-react - React specific linting rules for ESLint 2017-04-09
husky - 2017-04-09
electron-builder - A complete solution to package and build a ready f... 2017-04-09
jsfeat - JavaScript Computer Vision library. 2017-04-16
flakes - Flakes is an Admin Template Framework. A combinati... 2017-04-15
capstone - Capstone disassembly/disassembler framework: Core ... 2017-04-15
nginx-resources - A collection of resources covering Nginx, Nginx + ... 2017-04-15
utron - A lightweight MVC framework for Go(Golang) 2017-04-15
cfssl - CFSSL: Cloudflare's PKI and TLS toolkit ... 2017-04-15
HandBrake - HandBrake's main development repository ... 2017-04-15
Awesome-Networking - A curated list of awesome networking libraries, re... 2017-04-15
react-sortable-hoc - A set of higher-order components to turn any list ... 2017-04-15
jugglingdb - Multi-database ORM for nodejs: redis, mongodb, mys... 2017-04-15

 Back to top