Guest User

Untitled

a guest
Mar 4th, 2010
17,120
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 63.19 KB | None | 0 0
  1. #! /usr/bin/python
  2.  
  3. # aineptpdf.pyw
  4. # auto ineptpdf
  5. # aineptpdf, version 7.4
  6.  
  7. # To run this program install Python 2.6 from http://www.python.org/download/
  8. # and PyCrypto from http://www.voidspace.org.uk/python/modules.shtml#pycrypto
  9. # (make sure to install the version for Python 2.6).  Save this script file as
  10. # ineptpdf.pyw and double-click on it to run it.
  11.  
  12. # Revision history:
  13. #   1 - Initial release
  14. #   2 - Improved determination of key-generation algorithm
  15. #   3 - Correctly handle PDF >=1.5 cross-reference streams
  16. #   4 - Removal of ciando's personal ID
  17. #   5 - Automated decryption of a complete directory
  18. #   6.1 - backward compatibility for 1.7.1 and old adeptkey.der
  19. #   7 - Get cross reference streams and object streams working for input.
  20. #       Not yet supported on output but this only effects file size,
  21. #       not functionality. (anon2)
  22. #   7.1 - Correct a problem when an old trailer is not followed by startxref
  23. #   7.2 - Correct malformed Mac OS resource forks for Stanza (anon2)
  24. #       - Support for cross ref streams on output (decreases file size)
  25. #   7.3 - Correct bug in trailer with cross ref stream that caused the error
  26. #         "The root object is missing or invalid" in Adobe Reader. (anon2)
  27. #   7.4 - Force all generation numbers in output file to be 0, like in v6.
  28. #         Fallback code for wrong xref improved (search till last trailer
  29. #         instead of first) (anon2)
  30. """
  31. Decrypt Adobe ADEPT-encrypted PDF files.
  32. """
  33.  
  34. from __future__ import with_statement
  35.  
  36. __license__ = 'GPL v3'
  37.  
  38. import sys
  39. import os
  40. import re
  41. import zlib
  42. import struct
  43. import hashlib
  44. from itertools import chain, islice
  45. import xml.etree.ElementTree as etree
  46. import Tkinter
  47. import Tkconstants
  48. import tkFileDialog
  49. import tkMessageBox
  50.  
  51. try:
  52.     from Crypto.Cipher import ARC4
  53.     from Crypto.PublicKey import RSA
  54. except ImportError:
  55.     ARC4 = None
  56.     RSA = None
  57. try:
  58.     from cStringIO import StringIO
  59. except ImportError:
  60.     from StringIO import StringIO
  61.  
  62.  
  63. class ADEPTError(Exception):
  64.     pass
  65.  
  66. # Do we generate cross reference streams on output?
  67. # 0 = never
  68. # 1 = only if present in input
  69. # 2 = always
  70.  
  71. GEN_XREF_STM = 1
  72.  
  73. # This is the value for the current document
  74. gen_xref_stm = False # will be set in PDFSerializer
  75.  
  76. ###
  77. ### ASN.1 parsing code from tlslite
  78.  
  79. def bytesToNumber(bytes):
  80.     total = 0L
  81.     for byte in bytes:
  82.         total = (total << 8) + byte
  83.     return total
  84.  
  85. class ASN1Error(Exception):
  86.     pass
  87.  
  88. class ASN1Parser(object):
  89.     class Parser(object):
  90.         def __init__(self, bytes):
  91.             self.bytes = bytes
  92.             self.index = 0
  93.    
  94.         def get(self, length):
  95.             if self.index + length > len(self.bytes):
  96.                 raise ASN1Error("Error decoding ASN.1")
  97.             x = 0
  98.             for count in range(length):
  99.                 x <<= 8
  100.                 x |= self.bytes[self.index]
  101.                 self.index += 1
  102.             return x
  103.    
  104.         def getFixBytes(self, lengthBytes):
  105.             bytes = self.bytes[self.index : self.index+lengthBytes]
  106.             self.index += lengthBytes
  107.             return bytes
  108.    
  109.         def getVarBytes(self, lengthLength):
  110.             lengthBytes = self.get(lengthLength)
  111.             return self.getFixBytes(lengthBytes)
  112.    
  113.         def getFixList(self, length, lengthList):
  114.             l = [0] * lengthList
  115.             for x in range(lengthList):
  116.                 l[x] = self.get(length)
  117.             return l
  118.    
  119.         def getVarList(self, length, lengthLength):
  120.             lengthList = self.get(lengthLength)
  121.             if lengthList % length != 0:
  122.                 raise ASN1Error("Error decoding ASN.1")
  123.             lengthList = int(lengthList/length)
  124.             l = [0] * lengthList
  125.             for x in range(lengthList):
  126.                 l[x] = self.get(length)
  127.             return l
  128.    
  129.         def startLengthCheck(self, lengthLength):
  130.             self.lengthCheck = self.get(lengthLength)
  131.             self.indexCheck = self.index
  132.    
  133.         def setLengthCheck(self, length):
  134.             self.lengthCheck = length
  135.             self.indexCheck = self.index
  136.    
  137.         def stopLengthCheck(self):
  138.             if (self.index - self.indexCheck) != self.lengthCheck:
  139.                 raise ASN1Error("Error decoding ASN.1")
  140.    
  141.         def atLengthCheck(self):
  142.             if (self.index - self.indexCheck) < self.lengthCheck:
  143.                 return False
  144.             elif (self.index - self.indexCheck) == self.lengthCheck:
  145.                 return True
  146.             else:
  147.                 raise ASN1Error("Error decoding ASN.1")
  148.  
  149.     def __init__(self, bytes):
  150.         p = self.Parser(bytes)
  151.         p.get(1)
  152.         self.length = self._getASN1Length(p)
  153.         self.value = p.getFixBytes(self.length)
  154.  
  155.     def getChild(self, which):
  156.         p = self.Parser(self.value)
  157.         for x in range(which+1):
  158.             markIndex = p.index
  159.             p.get(1)
  160.             length = self._getASN1Length(p)
  161.             p.getFixBytes(length)
  162.         return ASN1Parser(p.bytes[markIndex:p.index])
  163.  
  164.     def _getASN1Length(self, p):
  165.         firstLength = p.get(1)
  166.         if firstLength<=127:
  167.             return firstLength
  168.         else:
  169.             lengthLength = firstLength & 0x7F
  170.             return p.get(lengthLength)
  171.  
  172. ###
  173. ### PDF parsing routines from pdfminer, with changes for EBX_HANDLER
  174.  
  175. ##  Utilities
  176. ##
  177. def choplist(n, seq):
  178.     '''Groups every n elements of the list.'''
  179.     r = []
  180.     for x in seq:
  181.         r.append(x)
  182.         if len(r) == n:
  183.             yield tuple(r)
  184.             r = []
  185.     return
  186.  
  187. def nunpack(s, default=0):
  188.     '''Unpacks up to 4 bytes big endian.'''
  189.     l = len(s)
  190.     if not l:
  191.         return default
  192.     elif l == 1:
  193.         return ord(s)
  194.     elif l == 2:
  195.         return struct.unpack('>H', s)[0]
  196.     elif l == 3:
  197.         return struct.unpack('>L', '\x00'+s)[0]
  198.     elif l == 4:
  199.         return struct.unpack('>L', s)[0]
  200.     else:
  201.         return TypeError('invalid length: %d' % l)
  202.  
  203.  
  204. STRICT = 1
  205.  
  206.  
  207. ##  PS Exceptions
  208. ##
  209. class PSException(Exception): pass
  210. class PSEOF(PSException): pass
  211. class PSSyntaxError(PSException): pass
  212. class PSTypeError(PSException): pass
  213. class PSValueError(PSException): pass
  214.  
  215.  
  216. ##  Basic PostScript Types
  217. ##
  218.  
  219. # PSLiteral
  220. class PSObject(object): pass
  221.  
  222. class PSLiteral(PSObject):
  223.     '''
  224.    PS literals (e.g. "/Name").
  225.    Caution: Never create these objects directly.
  226.    Use PSLiteralTable.intern() instead.
  227.    '''
  228.     def __init__(self, name):
  229.         self.name = name
  230.         return
  231.    
  232.     def __repr__(self):
  233.         name = []
  234.         for char in self.name:
  235.             if not char.isalnum():
  236.                 char = '#%02x' % ord(char)
  237.             name.append(char)
  238.         return '/%s' % ''.join(name)
  239.  
  240. # PSKeyword
  241. class PSKeyword(PSObject):
  242.     '''
  243.    PS keywords (e.g. "showpage").
  244.    Caution: Never create these objects directly.
  245.    Use PSKeywordTable.intern() instead.
  246.    '''
  247.     def __init__(self, name):
  248.         self.name = name
  249.         return
  250.    
  251.     def __repr__(self):
  252.         return self.name
  253.  
  254. # PSSymbolTable
  255. class PSSymbolTable(object):
  256.    
  257.     '''
  258.    Symbol table that stores PSLiteral or PSKeyword.
  259.    '''
  260.    
  261.     def __init__(self, classe):
  262.         self.dic = {}
  263.         self.classe = classe
  264.         return
  265.    
  266.     def intern(self, name):
  267.         if name in self.dic:
  268.             lit = self.dic[name]
  269.         else:
  270.             lit = self.classe(name)
  271.             self.dic[name] = lit
  272.         return lit
  273.  
  274. PSLiteralTable = PSSymbolTable(PSLiteral)
  275. PSKeywordTable = PSSymbolTable(PSKeyword)
  276. LIT = PSLiteralTable.intern
  277. KWD = PSKeywordTable.intern
  278. KEYWORD_BRACE_BEGIN = KWD('{')
  279. KEYWORD_BRACE_END = KWD('}')
  280. KEYWORD_ARRAY_BEGIN = KWD('[')
  281. KEYWORD_ARRAY_END = KWD(']')
  282. KEYWORD_DICT_BEGIN = KWD('<<')
  283. KEYWORD_DICT_END = KWD('>>')
  284.  
  285.  
  286. def literal_name(x):
  287.     if not isinstance(x, PSLiteral):
  288.         if STRICT:
  289.             raise PSTypeError('Literal required: %r' % x)
  290.         else:
  291.             return str(x)
  292.     return x.name
  293.  
  294. def keyword_name(x):
  295.     if not isinstance(x, PSKeyword):
  296.         if STRICT:
  297.             raise PSTypeError('Keyword required: %r' % x)
  298.         else:
  299.             return str(x)
  300.     return x.name
  301.  
  302.  
  303. ##  PSBaseParser
  304. ##
  305. EOL = re.compile(r'[\r\n]')
  306. SPC = re.compile(r'\s')
  307. NONSPC = re.compile(r'\S')
  308. HEX = re.compile(r'[0-9a-fA-F]')
  309. END_LITERAL = re.compile(r'[#/%\[\]()<>{}\s]')
  310. END_HEX_STRING = re.compile(r'[^\s0-9a-fA-F]')
  311. HEX_PAIR = re.compile(r'[0-9a-fA-F]{2}|.')
  312. END_NUMBER = re.compile(r'[^0-9]')
  313. END_KEYWORD = re.compile(r'[#/%\[\]()<>{}\s]')
  314. END_STRING = re.compile(r'[()\134]')
  315. OCT_STRING = re.compile(r'[0-7]')
  316. ESC_STRING = { 'b':8, 't':9, 'n':10, 'f':12, 'r':13, '(':40, ')':41, '\\':92 }
  317.  
  318. class PSBaseParser(object):
  319.  
  320.     '''
  321.    Most basic PostScript parser that performs only basic tokenization.
  322.    '''
  323.     BUFSIZ = 4096
  324.  
  325.     def __init__(self, fp):
  326.         self.fp = fp
  327.         self.seek(0)
  328.         return
  329.  
  330.     def __repr__(self):
  331.         return '<PSBaseParser: %r, bufpos=%d>' % (self.fp, self.bufpos)
  332.  
  333.     def flush(self):
  334.         return
  335.    
  336.     def close(self):
  337.         self.flush()
  338.         return
  339.    
  340.     def tell(self):
  341.         return self.bufpos+self.charpos
  342.  
  343.     def poll(self, pos=None, n=80):
  344.         pos0 = self.fp.tell()
  345.         if not pos:
  346.             pos = self.bufpos+self.charpos
  347.         self.fp.seek(pos)
  348.         print >>sys.stderr, 'poll(%d): %r' % (pos, self.fp.read(n))
  349.         self.fp.seek(pos0)
  350.         return
  351.  
  352.     def seek(self, pos):
  353.         '''
  354.        Seeks the parser to the given position.
  355.        '''
  356.         self.fp.seek(pos)
  357.         # reset the status for nextline()
  358.         self.bufpos = pos
  359.         self.buf = ''
  360.         self.charpos = 0
  361.         # reset the status for nexttoken()
  362.         self.parse1 = self.parse_main
  363.         self.tokens = []
  364.         return
  365.  
  366.     def fillbuf(self):
  367.         if self.charpos < len(self.buf): return
  368.         # fetch next chunk.
  369.         self.bufpos = self.fp.tell()
  370.         self.buf = self.fp.read(self.BUFSIZ)
  371.         if not self.buf:
  372.             raise PSEOF('Unexpected EOF')
  373.         self.charpos = 0
  374.         return
  375.    
  376.     def parse_main(self, s, i):
  377.         m = NONSPC.search(s, i)
  378.         if not m:
  379.             return (self.parse_main, len(s))
  380.         j = m.start(0)
  381.         c = s[j]
  382.         self.tokenstart = self.bufpos+j
  383.         if c == '%':
  384.             self.token = '%'
  385.             return (self.parse_comment, j+1)
  386.         if c == '/':
  387.             self.token = ''
  388.             return (self.parse_literal, j+1)
  389.         if c in '-+' or c.isdigit():
  390.             self.token = c
  391.             return (self.parse_number, j+1)
  392.         if c == '.':
  393.             self.token = c
  394.             return (self.parse_float, j+1)
  395.         if c.isalpha():
  396.             self.token = c
  397.             return (self.parse_keyword, j+1)
  398.         if c == '(':
  399.             self.token = ''
  400.             self.paren = 1
  401.             return (self.parse_string, j+1)
  402.         if c == '<':
  403.             self.token = ''
  404.             return (self.parse_wopen, j+1)
  405.         if c == '>':
  406.             self.token = ''
  407.             return (self.parse_wclose, j+1)
  408.         self.add_token(KWD(c))
  409.         return (self.parse_main, j+1)
  410.                            
  411.     def add_token(self, obj):
  412.         self.tokens.append((self.tokenstart, obj))
  413.         return
  414.    
  415.     def parse_comment(self, s, i):
  416.         m = EOL.search(s, i)
  417.         if not m:
  418.             self.token += s[i:]
  419.             return (self.parse_comment, len(s))
  420.         j = m.start(0)
  421.         self.token += s[i:j]
  422.         # We ignore comments.
  423.         #self.tokens.append(self.token)
  424.         return (self.parse_main, j)
  425.    
  426.     def parse_literal(self, s, i):
  427.         m = END_LITERAL.search(s, i)
  428.         if not m:
  429.             self.token += s[i:]
  430.             return (self.parse_literal, len(s))
  431.         j = m.start(0)
  432.         self.token += s[i:j]
  433.         c = s[j]
  434.         if c == '#':
  435.             self.hex = ''
  436.             return (self.parse_literal_hex, j+1)
  437.         self.add_token(LIT(self.token))
  438.         return (self.parse_main, j)
  439.    
  440.     def parse_literal_hex(self, s, i):
  441.         c = s[i]
  442.         if HEX.match(c) and len(self.hex) < 2:
  443.             self.hex += c
  444.             return (self.parse_literal_hex, i+1)
  445.         if self.hex:
  446.             self.token += chr(int(self.hex, 16))
  447.         return (self.parse_literal, i)
  448.  
  449.     def parse_number(self, s, i):
  450.         m = END_NUMBER.search(s, i)
  451.         if not m:
  452.             self.token += s[i:]
  453.             return (self.parse_number, len(s))
  454.         j = m.start(0)
  455.         self.token += s[i:j]
  456.         c = s[j]
  457.         if c == '.':
  458.             self.token += c
  459.             return (self.parse_float, j+1)
  460.         try:
  461.             self.add_token(int(self.token))
  462.         except ValueError:
  463.             pass
  464.         return (self.parse_main, j)
  465.     def parse_float(self, s, i):
  466.         m = END_NUMBER.search(s, i)
  467.         if not m:
  468.             self.token += s[i:]
  469.             return (self.parse_float, len(s))
  470.         j = m.start(0)
  471.         self.token += s[i:j]
  472.         self.add_token(float(self.token))
  473.         return (self.parse_main, j)
  474.    
  475.     def parse_keyword(self, s, i):
  476.         m = END_KEYWORD.search(s, i)
  477.         if not m:
  478.             self.token += s[i:]
  479.             return (self.parse_keyword, len(s))
  480.         j = m.start(0)
  481.         self.token += s[i:j]
  482.         if self.token == 'true':
  483.             token = True
  484.         elif self.token == 'false':
  485.             token = False
  486.         else:
  487.             token = KWD(self.token)
  488.         self.add_token(token)
  489.         return (self.parse_main, j)
  490.  
  491.     def parse_string(self, s, i):
  492.         m = END_STRING.search(s, i)
  493.         if not m:
  494.             self.token += s[i:]
  495.             return (self.parse_string, len(s))
  496.         j = m.start(0)
  497.         self.token += s[i:j]
  498.         c = s[j]
  499.         if c == '\\':
  500.             self.oct = ''
  501.             return (self.parse_string_1, j+1)
  502.         if c == '(':
  503.             self.paren += 1
  504.             self.token += c
  505.             return (self.parse_string, j+1)
  506.         if c == ')':
  507.             self.paren -= 1
  508.             if self.paren:
  509.                 self.token += c
  510.                 return (self.parse_string, j+1)
  511.         self.add_token(self.token)
  512.         return (self.parse_main, j+1)
  513.     def parse_string_1(self, s, i):
  514.         c = s[i]
  515.         if OCT_STRING.match(c) and len(self.oct) < 3:
  516.             self.oct += c
  517.             return (self.parse_string_1, i+1)
  518.         if self.oct:
  519.             self.token += chr(int(self.oct, 8))
  520.             return (self.parse_string, i)
  521.         if c in ESC_STRING:
  522.             self.token += chr(ESC_STRING[c])
  523.         return (self.parse_string, i+1)
  524.  
  525.     def parse_wopen(self, s, i):
  526.         c = s[i]
  527.         if c.isspace() or HEX.match(c):
  528.             return (self.parse_hexstring, i)
  529.         if c == '<':
  530.             self.add_token(KEYWORD_DICT_BEGIN)
  531.             i += 1
  532.         return (self.parse_main, i)
  533.  
  534.     def parse_wclose(self, s, i):
  535.         c = s[i]
  536.         if c == '>':
  537.             self.add_token(KEYWORD_DICT_END)
  538.             i += 1
  539.         return (self.parse_main, i)
  540.  
  541.     def parse_hexstring(self, s, i):
  542.         m = END_HEX_STRING.search(s, i)
  543.         if not m:
  544.             self.token += s[i:]
  545.             return (self.parse_hexstring, len(s))
  546.         j = m.start(0)
  547.         self.token += s[i:j]
  548.         token = HEX_PAIR.sub(lambda m: chr(int(m.group(0), 16)),
  549.                                                  SPC.sub('', self.token))
  550.         self.add_token(token)
  551.         return (self.parse_main, j)
  552.  
  553.     def nexttoken(self):
  554.         while not self.tokens:
  555.             self.fillbuf()
  556.             (self.parse1, self.charpos) = self.parse1(self.buf, self.charpos)
  557.         token = self.tokens.pop(0)
  558.         return token
  559.  
  560.     def nextline(self):
  561.         '''
  562.        Fetches a next line that ends either with \\r or \\n.
  563.        '''
  564.         linebuf = ''
  565.         linepos = self.bufpos + self.charpos
  566.         eol = False
  567.         while 1:
  568.             self.fillbuf()
  569.             if eol:
  570.                 c = self.buf[self.charpos]
  571.                 # handle '\r\n'
  572.                 if c == '\n':
  573.                     linebuf += c
  574.                     self.charpos += 1
  575.                 break
  576.             m = EOL.search(self.buf, self.charpos)
  577.             if m:
  578.                 linebuf += self.buf[self.charpos:m.end(0)]
  579.                 self.charpos = m.end(0)
  580.                 if linebuf[-1] == '\r':
  581.                     eol = True
  582.                 else:
  583.                     break
  584.             else:
  585.                 linebuf += self.buf[self.charpos:]
  586.                 self.charpos = len(self.buf)
  587.         return (linepos, linebuf)
  588.  
  589.     def revreadlines(self):
  590.         '''
  591.        Fetches a next line backword. This is used to locate
  592.        the trailers at the end of a file.
  593.        '''
  594.         self.fp.seek(0, 2)
  595.         pos = self.fp.tell()
  596.         buf = ''
  597.         while 0 < pos:
  598.             prevpos = pos
  599.             pos = max(0, pos-self.BUFSIZ)
  600.             self.fp.seek(pos)
  601.             s = self.fp.read(prevpos-pos)
  602.             if not s: break
  603.             while 1:
  604.                 n = max(s.rfind('\r'), s.rfind('\n'))
  605.                 if n == -1:
  606.                     buf = s + buf
  607.                     break
  608.                 yield s[n:]+buf
  609.                 s = s[:n]
  610.                 buf = ''
  611.         return
  612.  
  613.  
  614. ##  PSStackParser
  615. ##
  616. class PSStackParser(PSBaseParser):
  617.  
  618.     def __init__(self, fp):
  619.         PSBaseParser.__init__(self, fp)
  620.         self.reset()
  621.         return
  622.    
  623.     def reset(self):
  624.         self.context = []
  625.         self.curtype = None
  626.         self.curstack = []
  627.         self.results = []
  628.         return
  629.  
  630.     def seek(self, pos):
  631.         PSBaseParser.seek(self, pos)
  632.         self.reset()
  633.         return
  634.  
  635.     def push(self, *objs):
  636.         self.curstack.extend(objs)
  637.         return
  638.     def pop(self, n):
  639.         objs = self.curstack[-n:]
  640.         self.curstack[-n:] = []
  641.         return objs
  642.     def popall(self):
  643.         objs = self.curstack
  644.         self.curstack = []
  645.         return objs
  646.     def add_results(self, *objs):
  647.         self.results.extend(objs)
  648.         return
  649.  
  650.     def start_type(self, pos, type):
  651.         self.context.append((pos, self.curtype, self.curstack))
  652.         (self.curtype, self.curstack) = (type, [])
  653.         return
  654.     def end_type(self, type):
  655.         if self.curtype != type:
  656.             raise PSTypeError('Type mismatch: %r != %r' % (self.curtype, type))
  657.         objs = [ obj for (_,obj) in self.curstack ]
  658.         (pos, self.curtype, self.curstack) = self.context.pop()
  659.         return (pos, objs)
  660.  
  661.     def do_keyword(self, pos, token):
  662.         return
  663.    
  664.     def nextobject(self, direct=False):
  665.         '''
  666.        Yields a list of objects: keywords, literals, strings,
  667.        numbers, arrays and dictionaries. Arrays and dictionaries
  668.        are represented as Python sequence and dictionaries.
  669.        '''
  670.         while not self.results:
  671.             (pos, token) = self.nexttoken()
  672.             #print (pos,token), (self.curtype, self.curstack)
  673.             if (isinstance(token, int) or
  674.                     isinstance(token, float) or
  675.                     isinstance(token, bool) or
  676.                     isinstance(token, str) or
  677.                     isinstance(token, PSLiteral)):
  678.                 # normal token
  679.                 self.push((pos, token))
  680.             elif token == KEYWORD_ARRAY_BEGIN:
  681.                 # begin array
  682.                 self.start_type(pos, 'a')
  683.             elif token == KEYWORD_ARRAY_END:
  684.                 # end array
  685.                 try:
  686.                     self.push(self.end_type('a'))
  687.                 except PSTypeError:
  688.                     if STRICT: raise
  689.             elif token == KEYWORD_DICT_BEGIN:
  690.                 # begin dictionary
  691.                 self.start_type(pos, 'd')
  692.             elif token == KEYWORD_DICT_END:
  693.                 # end dictionary
  694.                 try:
  695.                     (pos, objs) = self.end_type('d')
  696.                     if len(objs) % 2 != 0:
  697.                         raise PSSyntaxError(
  698.                             'Invalid dictionary construct: %r' % objs)
  699.                     d = dict((literal_name(k), v) \
  700.                                  for (k,v) in choplist(2, objs))
  701.                     self.push((pos, d))
  702.                 except PSTypeError:
  703.                     if STRICT: raise
  704.             else:
  705.                 self.do_keyword(pos, token)
  706.             if self.context:
  707.                 continue
  708.             else:
  709.                 if direct:
  710.                     return self.pop(1)[0]
  711.                 self.flush()
  712.         obj = self.results.pop(0)
  713.         return obj
  714.  
  715.  
  716. LITERAL_CRYPT = PSLiteralTable.intern('Crypt')
  717. LITERALS_FLATE_DECODE = (PSLiteralTable.intern('FlateDecode'), PSLiteralTable.intern('Fl'))
  718. LITERALS_LZW_DECODE = (PSLiteralTable.intern('LZWDecode'), PSLiteralTable.intern('LZW'))
  719. LITERALS_ASCII85_DECODE = (PSLiteralTable.intern('ASCII85Decode'), PSLiteralTable.intern('A85'))
  720.  
  721.  
  722. ##  PDF Objects
  723. ##
  724. class PDFObject(PSObject): pass
  725.  
  726. class PDFException(PSException): pass
  727. class PDFTypeError(PDFException): pass
  728. class PDFValueError(PDFException): pass
  729. class PDFNotImplementedError(PSException): pass
  730.  
  731.  
  732. ##  PDFObjRef
  733. ##
  734. class PDFObjRef(PDFObject):
  735.    
  736.     def __init__(self, doc, objid, genno):
  737.         if objid == 0:
  738.             if STRICT:
  739.                 raise PDFValueError('PDF object id cannot be 0.')
  740.         self.doc = doc
  741.         self.objid = objid
  742.         self.genno = genno
  743.         return
  744.  
  745.     def __repr__(self):
  746.         return '<PDFObjRef:%d %d>' % (self.objid, self.genno)
  747.  
  748.     def resolve(self):
  749.         return self.doc.getobj(self.objid)
  750.  
  751.  
  752. # resolve
  753. def resolve1(x):
  754.     '''
  755.    Resolve an object. If this is an array or dictionary,
  756.    it may still contains some indirect objects inside.
  757.    '''
  758.     while isinstance(x, PDFObjRef):
  759.         x = x.resolve()
  760.     return x
  761.  
  762. def resolve_all(x):
  763.     '''
  764.    Recursively resolve X and all the internals.
  765.    Make sure there is no indirect reference within the nested object.
  766.    This procedure might be slow.
  767.    '''
  768.     while isinstance(x, PDFObjRef):
  769.         x = x.resolve()
  770.     if isinstance(x, list):
  771.         x = [ resolve_all(v) for v in x ]
  772.     elif isinstance(x, dict):
  773.         for (k,v) in x.iteritems():
  774.             x[k] = resolve_all(v)
  775.     return x
  776.  
  777. def decipher_all(decipher, objid, genno, x):
  778.     '''
  779.    Recursively decipher X.
  780.    '''
  781.     if isinstance(x, str):
  782.         return decipher(objid, genno, x)
  783.     decf = lambda v: decipher_all(decipher, objid, genno, v)
  784.     if isinstance(x, list):
  785.         x = [decf(v) for v in x]
  786.     elif isinstance(x, dict):
  787.         x = dict((k, decf(v)) for (k, v) in x.iteritems())
  788.     return x
  789.  
  790. # Type cheking
  791. def int_value(x):
  792.     x = resolve1(x)
  793.     if not isinstance(x, int):
  794.         if STRICT:
  795.             raise PDFTypeError('Integer required: %r' % x)
  796.         return 0
  797.     return x
  798.  
  799. def float_value(x):
  800.     x = resolve1(x)
  801.     if not isinstance(x, float):
  802.         if STRICT:
  803.             raise PDFTypeError('Float required: %r' % x)
  804.         return 0.0
  805.     return x
  806.  
  807. def num_value(x):
  808.     x = resolve1(x)
  809.     if not (isinstance(x, int) or isinstance(x, float)):
  810.         if STRICT:
  811.             raise PDFTypeError('Int or Float required: %r' % x)
  812.         return 0
  813.     return x
  814.  
  815. def str_value(x):
  816.     x = resolve1(x)
  817.     if not isinstance(x, str):
  818.         if STRICT:
  819.             raise PDFTypeError('String required: %r' % x)
  820.         return ''
  821.     return x
  822.  
  823. def list_value(x):
  824.     x = resolve1(x)
  825.     if not (isinstance(x, list) or isinstance(x, tuple)):
  826.         if STRICT:
  827.             raise PDFTypeError('List required: %r' % x)
  828.         return []
  829.     return x
  830.  
  831. def dict_value(x):
  832.     x = resolve1(x)
  833.     if not isinstance(x, dict):
  834.         if STRICT:
  835.             raise PDFTypeError('Dict required: %r' % x)
  836.         return {}
  837.     return x
  838.  
  839. def stream_value(x):
  840.     x = resolve1(x)
  841.     if not isinstance(x, PDFStream):
  842.         if STRICT:
  843.             raise PDFTypeError('PDFStream required: %r' % x)
  844.         return PDFStream({}, '')
  845.     return x
  846.  
  847. # ascii85decode(data)
  848. def ascii85decode(data):
  849.   n = b = 0
  850.   out = ''
  851.   for c in data:
  852.     if '!' <= c and c <= 'u':
  853.       n += 1
  854.       b = b*85+(ord(c)-33)
  855.       if n == 5:
  856.         out += struct.pack('>L',b)
  857.         n = b = 0
  858.     elif c == 'z':
  859.       assert n == 0
  860.       out += '\0\0\0\0'
  861.     elif c == '~':
  862.       if n:
  863.         for _ in range(5-n):
  864.           b = b*85+84
  865.         out += struct.pack('>L',b)[:n-1]
  866.       break
  867.   return out
  868.  
  869.  
  870. ##  PDFStream type
  871. ##
  872. class PDFStream(PDFObject):
  873.    
  874.     def __init__(self, dic, rawdata, decipher=None):
  875.         length = int_value(dic.get('Length', 0))
  876.         eol = rawdata[length:]
  877.         if eol in ('\r', '\n', '\r\n'):
  878.             rawdata = rawdata[:length]
  879.         if length != len(rawdata):
  880.             print >>sys.stderr, "[warning] data length mismatch"
  881.         self.dic = dic
  882.         self.rawdata = rawdata
  883.         self.decipher = decipher
  884.         self.data = None
  885.         self.decdata = None
  886.         self.objid = None
  887.         self.genno = None
  888.         return
  889.  
  890.     def set_objid(self, objid, genno):
  891.         self.objid = objid
  892.         self.genno = genno
  893.         return
  894.    
  895.     def __repr__(self):
  896.         if self.rawdata:
  897.             return '<PDFStream(%r): raw=%d, %r>' % \
  898.                    (self.objid, len(self.rawdata), self.dic)
  899.         else:
  900.             return '<PDFStream(%r): data=%d, %r>' % \
  901.                    (self.objid, len(self.data), self.dic)
  902.  
  903.     def decode(self):
  904.         assert self.data is None and self.rawdata is not None
  905.         data = self.rawdata
  906.         if self.decipher:
  907.             # Handle encryption
  908.             data = self.decipher(self.objid, self.genno, data)
  909.             if gen_xref_stm:
  910.                 self.decdata = data # keep decrypted data
  911.         if 'Filter' not in self.dic:
  912.             self.data = data
  913.             self.rawdata = None
  914.             return
  915.         filters = self.dic['Filter']
  916.         if not isinstance(filters, list):
  917.             filters = [ filters ]
  918.         for f in filters:
  919.             if f in LITERALS_FLATE_DECODE:
  920.                 # will get errors if the document is encrypted.
  921.                 data = zlib.decompress(data)
  922.             elif f in LITERALS_LZW_DECODE:
  923.                 data = ''.join(LZWDecoder(StringIO(data)).run())
  924.             elif f in LITERALS_ASCII85_DECODE:
  925.                 data = ascii85decode(data)
  926.             elif f == LITERAL_CRYPT:
  927.                 raise PDFNotImplementedError('/Crypt filter is unsupported')
  928.             else:
  929.                 raise PDFNotImplementedError('Unsupported filter: %r' % f)
  930.             # apply predictors
  931.             if 'DP' in self.dic:
  932.                 params = self.dic['DP']
  933.             else:
  934.                 params = self.dic.get('DecodeParms', {})
  935.             if 'Predictor' in params:
  936.                 pred = int_value(params['Predictor'])
  937.                 if pred:
  938.                     if pred != 12:
  939.                         raise PDFNotImplementedError(
  940.                             'Unsupported predictor: %r' % pred)
  941.                     if 'Columns' not in params:
  942.                         raise PDFValueError(
  943.                             'Columns undefined for predictor=12')
  944.                     columns = int_value(params['Columns'])
  945.                     buf = ''
  946.                     ent0 = '\x00' * columns
  947.                     for i in xrange(0, len(data), columns+1):
  948.                         pred = data[i]
  949.                         ent1 = data[i+1:i+1+columns]
  950.                         if pred == '\x02':
  951.                             ent1 = ''.join(chr((ord(a)+ord(b)) & 255) \
  952.                                                for (a,b) in zip(ent0,ent1))
  953.                         buf += ent1
  954.                         ent0 = ent1
  955.                     data = buf
  956.         self.data = data
  957.         self.rawdata = None
  958.         return
  959.  
  960.     def get_data(self):
  961.         if self.data is None:
  962.             self.decode()
  963.         return self.data
  964.  
  965.     def get_rawdata(self):
  966.         return self.rawdata
  967.  
  968.     def get_decdata(self):
  969.         if self.decdata is not None:
  970.             return self.decdata
  971.         data = self.rawdata
  972.         if self.decipher and data:
  973.             # Handle encryption
  974.             data = self.decipher(self.objid, self.genno, data)
  975.         return data
  976.  
  977.        
  978. ##  PDF Exceptions
  979. ##
  980. class PDFSyntaxError(PDFException): pass
  981. class PDFNoValidXRef(PDFSyntaxError): pass
  982. class PDFEncryptionError(PDFException): pass
  983. class PDFPasswordIncorrect(PDFEncryptionError): pass
  984.  
  985. # some predefined literals and keywords.
  986. LITERAL_OBJSTM = PSLiteralTable.intern('ObjStm')
  987. LITERAL_XREF = PSLiteralTable.intern('XRef')
  988. LITERAL_PAGE = PSLiteralTable.intern('Page')
  989. LITERAL_PAGES = PSLiteralTable.intern('Pages')
  990. LITERAL_CATALOG = PSLiteralTable.intern('Catalog')
  991.  
  992.  
  993. ##  XRefs
  994. ##
  995.  
  996. ##  PDFXRef
  997. ##
  998. class PDFXRef(object):
  999.  
  1000.     def __init__(self):
  1001.         self.offsets = None
  1002.         return
  1003.  
  1004.     def __repr__(self):
  1005.         return '<PDFXRef: objs=%d>' % len(self.offsets)
  1006.  
  1007.     def objids(self):
  1008.         return self.offsets.iterkeys()
  1009.  
  1010.     def load(self, parser):
  1011.         self.offsets = {}
  1012.         while 1:
  1013.             try:
  1014.                 (pos, line) = parser.nextline()
  1015.             except PSEOF:
  1016.                 raise PDFNoValidXRef('Unexpected EOF - file corrupted?')
  1017.             if not line:
  1018.                 raise PDFNoValidXRef('Premature eof: %r' % parser)
  1019.             if line.startswith('trailer'):
  1020.                 parser.seek(pos)
  1021.                 break
  1022.             f = line.strip().split(' ')
  1023.             if len(f) != 2:
  1024.                 raise PDFNoValidXRef('Trailer not found: %r: line=%r' % (parser, line))
  1025.             try:
  1026.                 (start, nobjs) = map(int, f)
  1027.             except ValueError:
  1028.                 raise PDFNoValidXRef('Invalid line: %r: line=%r' % (parser, line))
  1029.             for objid in xrange(start, start+nobjs):
  1030.                 try:
  1031.                     (_, line) = parser.nextline()
  1032.                 except PSEOF:
  1033.                     raise PDFNoValidXRef('Unexpected EOF - file corrupted?')
  1034.                 f = line.strip().split(' ')
  1035.                 if len(f) != 3:
  1036.                     raise PDFNoValidXRef('Invalid XRef format: %r, line=%r' % (parser, line))
  1037.                 (pos, genno, use) = f
  1038.                 if use != 'n': continue
  1039.                 self.offsets[objid] = (int(genno), int(pos))
  1040.         self.load_trailer(parser)
  1041.         return
  1042.    
  1043.     KEYWORD_TRAILER = PSKeywordTable.intern('trailer')
  1044.     def load_trailer(self, parser):
  1045.         try:
  1046.             (_,kwd) = parser.nexttoken()
  1047.             assert kwd is self.KEYWORD_TRAILER
  1048.             (_,dic) = parser.nextobject(direct=True)
  1049.         except PSEOF:
  1050.             x = parser.pop(1)
  1051.             if not x:
  1052.                 raise PDFNoValidXRef('Unexpected EOF - file corrupted')
  1053.             (_,dic) = x[0]
  1054.         self.trailer = dict_value(dic)
  1055.         return
  1056.  
  1057.     def getpos(self, objid):
  1058.         try:
  1059.             (genno, pos) = self.offsets[objid]
  1060.         except KeyError:
  1061.             raise
  1062.         return (None, pos)
  1063.  
  1064.  
  1065. ##  PDFXRefStream
  1066. ##
  1067. class PDFXRefStream(object):
  1068.  
  1069.     def __init__(self):
  1070.         self.index = None
  1071.         self.data = None
  1072.         self.entlen = None
  1073.         self.fl1 = self.fl2 = self.fl3 = None
  1074.         return
  1075.  
  1076.     def __repr__(self):
  1077.         return '<PDFXRef: objids=%s>' % self.index
  1078.  
  1079.     def objids(self):
  1080.         for first, size in self.index:
  1081.             for objid in xrange(first, first + size):
  1082.                 yield objid
  1083.    
  1084.     def load(self, parser, debug=0):
  1085.         (_,objid) = parser.nexttoken() # ignored
  1086.         (_,genno) = parser.nexttoken() # ignored
  1087.         (_,kwd) = parser.nexttoken()
  1088.         (_,stream) = parser.nextobject()
  1089.         if not isinstance(stream, PDFStream) or \
  1090.            stream.dic['Type'] is not LITERAL_XREF:
  1091.             raise PDFNoValidXRef('Invalid PDF stream spec.')
  1092.         size = stream.dic['Size']
  1093.         index = stream.dic.get('Index', (0,size))
  1094.         self.index = zip(islice(index, 0, None, 2),
  1095.                          islice(index, 1, None, 2))
  1096.         (self.fl1, self.fl2, self.fl3) = stream.dic['W']
  1097.         self.data = stream.get_data()
  1098.         self.entlen = self.fl1+self.fl2+self.fl3
  1099.         self.trailer = stream.dic
  1100.         return
  1101.    
  1102.     def getpos(self, objid):
  1103.         offset = 0
  1104.         for first, size in self.index:
  1105.             if first <= objid  and objid < (first + size):
  1106.                 break
  1107.             offset += size
  1108.         else:
  1109.             raise KeyError(objid)
  1110.         i = self.entlen * ((objid - first) + offset)
  1111.         ent = self.data[i:i+self.entlen]
  1112.         f1 = nunpack(ent[:self.fl1], 1)
  1113.         if f1 == 1:
  1114.             pos = nunpack(ent[self.fl1:self.fl1+self.fl2])
  1115.             genno = nunpack(ent[self.fl1+self.fl2:])
  1116.             return (None, pos)
  1117.         elif f1 == 2:
  1118.             objid = nunpack(ent[self.fl1:self.fl1+self.fl2])
  1119.             index = nunpack(ent[self.fl1+self.fl2:])
  1120.             return (objid, index)
  1121.         # this is a free object
  1122.         raise KeyError(objid)
  1123.  
  1124.  
  1125. ##  PDFDocument
  1126. ##
  1127. ##  A PDFDocument object represents a PDF document.
  1128. ##  Since a PDF file is usually pretty big, normally it is not loaded
  1129. ##  at once. Rather it is parsed dynamically as processing goes.
  1130. ##  A PDF parser is associated with the document.
  1131. ##
  1132. class PDFDocument(object):
  1133.  
  1134.     def __init__(self):
  1135.         self.xrefs = []
  1136.         self.objs = {}
  1137.         self.parsed_objs = {}
  1138.         self.root = None
  1139.         self.catalog = None
  1140.         self.parser = None
  1141.         self.encryption = None
  1142.         self.decipher = None
  1143.         self.ready = False
  1144.         return
  1145.  
  1146.     # set_parser(parser)
  1147.     #   Associates the document with an (already initialized) parser object.
  1148.     def set_parser(self, parser):
  1149.         if self.parser: return
  1150.         self.parser = parser
  1151.         # The document is set to be temporarily ready during collecting
  1152.         # all the basic information about the document, e.g.
  1153.         # the header, the encryption information, and the access rights
  1154.         # for the document.
  1155.         self.ready = True
  1156.         # Retrieve the information of each header that was appended
  1157.         # (maybe multiple times) at the end of the document.
  1158.         self.xrefs = parser.read_xref()
  1159.         for xref in self.xrefs:
  1160.             trailer = xref.trailer
  1161.             if not trailer: continue
  1162.  
  1163.             # If there's an encryption info, remember it.
  1164.             if 'Encrypt' in trailer:
  1165.                 #assert not self.encryption
  1166.                 self.encryption = (list_value(trailer['ID']),
  1167.                                    dict_value(trailer['Encrypt']))
  1168.             if 'Root' in trailer:
  1169.                 self.set_root(dict_value(trailer['Root']))
  1170.                 break
  1171.         else:
  1172.             raise PDFSyntaxError('No /Root object! - Is this really a PDF?')
  1173.         # The document is set to be non-ready again, until all the
  1174.         # proper initialization (asking the password key and
  1175.         # verifying the access permission, so on) is finished.
  1176.         self.ready = False
  1177.         return
  1178.  
  1179.     # set_root(root)
  1180.     #   Set the Root dictionary of the document.
  1181.     #   Each PDF file must have exactly one /Root dictionary.
  1182.     def set_root(self, root):
  1183.         self.root = root
  1184.         self.catalog = dict_value(self.root)
  1185.         if self.catalog.get('Type') is not LITERAL_CATALOG:
  1186.             if STRICT:
  1187.                 raise PDFSyntaxError('Catalog not found!')
  1188.         return
  1189.    
  1190.     # initialize(password='')
  1191.     #   Perform the initialization with a given password.
  1192.     #   This step is mandatory even if there's no password associated
  1193.     #   with the document.
  1194.     def initialize(self, password=''):
  1195.         if not self.encryption:
  1196.             self.is_printable = self.is_modifiable = self.is_extractable = True
  1197.             self.ready = True
  1198.             return
  1199.         (docid, param) = self.encryption
  1200.         type = literal_name(param['Filter'])
  1201.         if type == 'Standard':
  1202.             return self.initialize_standard(password, docid, param)
  1203.         if type == 'EBX_HANDLER':
  1204.             return self.initialize_ebx(password, docid, param)
  1205.         raise PDFEncryptionError('Unknown filter: param=%r' % param)
  1206.  
  1207.     def initialize_ebx(self, password, docid, param):
  1208.         self.is_printable = self.is_modifiable = self.is_extractable = True
  1209.         with open(password, 'rb') as f:
  1210.             keyder = f.read()
  1211.         key = ASN1Parser([ord(x) for x in keyder])
  1212.         key = [bytesToNumber(key.getChild(x).value) for x in xrange(1, 4)]
  1213.         rsa = RSA.construct(key)
  1214.         length = int_value(param.get('Length', 0)) / 8
  1215.         rights = str_value(param.get('ADEPT_LICENSE')).decode('base64')
  1216.         rights = zlib.decompress(rights, -15)
  1217.         rights = etree.fromstring(rights)
  1218.         expr = './/{http://ns.adobe.com/adept}encryptedKey'
  1219.         bookkey = ''.join(rights.findtext(expr)).decode('base64')
  1220.         bookkey = rsa.decrypt(bookkey)
  1221.         if bookkey[0] != '\x02':
  1222.             raise ADEPTError('error decrypting book session key')
  1223.         index = bookkey.index('\0') + 1
  1224.         bookkey = bookkey[index:]
  1225.         ebx_V = int_value(param.get('V', 4))
  1226.         ebx_type = int_value(param.get('EBX_ENCRYPTIONTYPE', 6))
  1227.         # added because of the booktype / decryption book session key error
  1228.         if ebx_V == 3:
  1229.             V = 3        
  1230.         elif ebx_V < 4 or ebx_type < 6:
  1231.             V = ord(bookkey[0])
  1232.             bookkey = bookkey[1:]
  1233.         else:
  1234.             V = 2
  1235.         if length and len(bookkey) != length:
  1236.             raise ADEPTError('error decrypting book session key')
  1237.         self.decrypt_key = bookkey
  1238.         self.genkey = self.genkey_v3 if V == 3 else self.genkey_v2
  1239.         self.decipher = self.decrypt_rc4
  1240.         self.ready = True
  1241.         return
  1242.  
  1243.    
  1244.     PASSWORD_PADDING = '(\xbfN^Nu\x8aAd\x00NV\xff\xfa\x01\x08..' \
  1245.                        '\x00\xb6\xd0h>\x80/\x0c\xa9\xfedSiz'
  1246.     def initialize_standard(self, password, docid, param):
  1247.         V = int_value(param.get('V', 0))
  1248.         if not (V == 1 or V == 2):
  1249.             raise PDFEncryptionError('Unknown algorithm: param=%r' % param)
  1250.         length = int_value(param.get('Length', 40)) # Key length (bits)
  1251.         O = str_value(param['O'])
  1252.         R = int_value(param['R']) # Revision
  1253.         if 5 <= R:
  1254.             raise PDFEncryptionError('Unknown revision: %r' % R)
  1255.         U = str_value(param['U'])
  1256.         P = int_value(param['P'])
  1257.         self.is_printable = bool(P & 4)        
  1258.         self.is_modifiable = bool(P & 8)
  1259.         self.is_extractable = bool(P & 16)
  1260.         # Algorithm 3.2
  1261.         password = (password+self.PASSWORD_PADDING)[:32] # 1
  1262.         hash = hashlib.md5(password) # 2
  1263.         hash.update(O) # 3
  1264.         hash.update(struct.pack('<l', P)) # 4
  1265.         hash.update(docid[0]) # 5
  1266.         if 4 <= R:
  1267.             # 6
  1268.             raise PDFNotImplementedError(
  1269.                 'Revision 4 encryption is currently unsupported')
  1270.         if 3 <= R:
  1271.             # 8
  1272.             for _ in xrange(50):
  1273.                 hash = hashlib.md5(hash.digest()[:length/8])
  1274.         key = hash.digest()[:length/8]
  1275.         if R == 2:
  1276.             # Algorithm 3.4
  1277.             u1 = ARC4.new(key).decrypt(password)
  1278.         elif R == 3:
  1279.             # Algorithm 3.5
  1280.             hash = hashlib.md5(self.PASSWORD_PADDING) # 2
  1281.             hash.update(docid[0]) # 3
  1282.             x = ARC4.new(key).decrypt(hash.digest()[:16]) # 4
  1283.             for i in xrange(1,19+1):
  1284.                 k = ''.join( chr(ord(c) ^ i) for c in key )
  1285.                 x = ARC4.new(k).decrypt(x)
  1286.             u1 = x+x # 32bytes total
  1287.         if R == 2:
  1288.             is_authenticated = (u1 == U)
  1289.         else:
  1290.             is_authenticated = (u1[:16] == U[:16])
  1291.         if not is_authenticated:
  1292.             raise PDFPasswordIncorrect
  1293.         self.decrypt_key = key
  1294.         self.genkey = self.genkey_v2
  1295.         self.decipher = self.decipher_rc4  # XXX may be AES
  1296.         self.ready = True
  1297.         return
  1298.  
  1299.     def genkey_v2(self, objid, genno):
  1300.         objid = struct.pack('<L', objid)[:3]
  1301.         genno = struct.pack('<L', genno)[:2]
  1302.         key = self.decrypt_key + objid + genno
  1303.         hash = hashlib.md5(key)
  1304.         key = hash.digest()[:min(len(self.decrypt_key) + 5, 16)]
  1305.         return key
  1306.    
  1307.     def genkey_v3(self, objid, genno):
  1308.         objid = struct.pack('<L', objid ^ 0x3569ac)
  1309.         genno = struct.pack('<L', genno ^ 0xca96)
  1310.         key = self.decrypt_key
  1311.         key += objid[0] + genno[0] + objid[1] + genno[1] + objid[2] + 'sAlT'
  1312.         hash = hashlib.md5(key)
  1313.         key = hash.digest()[:min(len(self.decrypt_key) + 5, 16)]
  1314.         return key
  1315.    
  1316.     def decrypt_rc4(self, objid, genno, data):
  1317.         key = self.genkey(objid, genno)
  1318.         return ARC4.new(key).decrypt(data)
  1319.    
  1320.     KEYWORD_OBJ = PSKeywordTable.intern('obj')
  1321.     def getobj(self, objid):
  1322.         if not self.ready:
  1323.             raise PDFException('PDFDocument not initialized')
  1324.         #assert self.xrefs
  1325.         if objid in self.objs:
  1326.             genno = 0
  1327.             obj = self.objs[objid]
  1328.         else:
  1329.             for xref in self.xrefs:
  1330.                 try:
  1331.                     (stmid, index) = xref.getpos(objid)
  1332.                     break
  1333.                 except KeyError:
  1334.                     pass
  1335.             else:
  1336.                 #if STRICT:
  1337.                 #    raise PDFSyntaxError('Cannot locate objid=%r' % objid)
  1338.                 return None
  1339.             if stmid:
  1340.                 if gen_xref_stm:
  1341.                     return PDFObjStmRef(objid, stmid, index)
  1342. # Stuff from pdfminer: extract objects from object stream
  1343.                 stream = stream_value(self.getobj(stmid))
  1344.                 if stream.dic.get('Type') is not LITERAL_OBJSTM:
  1345.                     if STRICT:
  1346.                         raise PDFSyntaxError('Not a stream object: %r' % stream)
  1347.                 try:
  1348.                     n = stream.dic['N']
  1349.                 except KeyError:
  1350.                     if STRICT:
  1351.                         raise PDFSyntaxError('N is not defined: %r' % stream)
  1352.                     n = 0
  1353.  
  1354.                 if stmid in self.parsed_objs:
  1355.                     objs = self.parsed_objs[stmid]
  1356.                 else:
  1357.                     parser = PDFObjStrmParser(stream.get_data(), self)
  1358.                     objs = []
  1359.                     try:
  1360.                         while 1:
  1361.                             (_,obj) = parser.nextobject()
  1362.                             objs.append(obj)
  1363.                     except PSEOF:
  1364.                         pass
  1365.                     self.parsed_objs[stmid] = objs
  1366.                 genno = 0
  1367.                 i = n*2+index
  1368.                 try:
  1369.                     obj = objs[i]
  1370.                 except IndexError:
  1371.                     raise PDFSyntaxError('Invalid object number: objid=%r' % (objid))
  1372.                 if isinstance(obj, PDFStream):
  1373.                     obj.set_objid(objid, 0)
  1374. ###
  1375.             else:
  1376.                 self.parser.seek(index)
  1377.                 (_,objid1) = self.parser.nexttoken() # objid
  1378.                 (_,genno) = self.parser.nexttoken() # genno
  1379.                 #assert objid1 == objid, (objid, objid1)
  1380.                 (_,kwd) = self.parser.nexttoken()
  1381.                 if kwd is not self.KEYWORD_OBJ:
  1382.                     raise PDFSyntaxError(
  1383.                         'Invalid object spec: offset=%r' % index)
  1384.                 (_,obj) = self.parser.nextobject()
  1385.                 if isinstance(obj, PDFStream):
  1386.                     obj.set_objid(objid, genno)
  1387.                 if self.decipher:
  1388.                     obj = decipher_all(self.decipher, objid, genno, obj)
  1389.             self.objs[objid] = obj
  1390.         return obj
  1391.  
  1392. class PDFObjStmRef(object):
  1393.     maxindex = 0
  1394.     def __init__(self, objid, stmid, index):
  1395.         self.objid = objid
  1396.         self.stmid = stmid
  1397.         self.index = index
  1398.         if index > PDFObjStmRef.maxindex:
  1399.             PDFObjStmRef.maxindex = index
  1400.  
  1401.    
  1402. ##  PDFParser
  1403. ##
  1404. class PDFParser(PSStackParser):
  1405.  
  1406.     def __init__(self, doc, fp):
  1407.         PSStackParser.__init__(self, fp)
  1408.         self.doc = doc
  1409.         self.doc.set_parser(self)
  1410.         return
  1411.  
  1412.     def __repr__(self):
  1413.         return '<PDFParser>'
  1414.  
  1415.     KEYWORD_R = PSKeywordTable.intern('R')
  1416.     KEYWORD_ENDOBJ = PSKeywordTable.intern('endobj')
  1417.     KEYWORD_STREAM = PSKeywordTable.intern('stream')
  1418.     KEYWORD_XREF = PSKeywordTable.intern('xref')
  1419.     KEYWORD_STARTXREF = PSKeywordTable.intern('startxref')
  1420.     def do_keyword(self, pos, token):
  1421.         if token in (self.KEYWORD_XREF, self.KEYWORD_STARTXREF):
  1422.             self.add_results(*self.pop(1))
  1423.             return
  1424.         if token is self.KEYWORD_ENDOBJ:
  1425.             self.add_results(*self.pop(4))
  1426.             return
  1427.        
  1428.         if token is self.KEYWORD_R:
  1429.             # reference to indirect object
  1430.             try:
  1431.                 ((_,objid), (_,genno)) = self.pop(2)
  1432.                 (objid, genno) = (int(objid), int(genno))
  1433.                 obj = PDFObjRef(self.doc, objid, genno)
  1434.                 self.push((pos, obj))
  1435.             except PSSyntaxError:
  1436.                 pass
  1437.             return
  1438.            
  1439.         if token is self.KEYWORD_STREAM:
  1440.             # stream object
  1441.             ((_,dic),) = self.pop(1)
  1442.             dic = dict_value(dic)
  1443.             try:
  1444.                 objlen = int_value(dic['Length'])
  1445.             except KeyError:
  1446.                 if STRICT:
  1447.                     raise PDFSyntaxError('/Length is undefined: %r' % dic)
  1448.                 objlen = 0
  1449.             self.seek(pos)
  1450.             try:
  1451.                 (_, line) = self.nextline()  # 'stream'
  1452.             except PSEOF:
  1453.                 if STRICT:
  1454.                     raise PDFSyntaxError('Unexpected EOF')
  1455.                 return
  1456.             pos += len(line)
  1457.             self.fp.seek(pos)
  1458.             data = self.fp.read(objlen)
  1459.             self.seek(pos+objlen)
  1460.             while 1:
  1461.                 try:
  1462.                     (linepos, line) = self.nextline()
  1463.                 except PSEOF:
  1464.                     if STRICT:
  1465.                         raise PDFSyntaxError('Unexpected EOF')
  1466.                     break
  1467.                 if 'endstream' in line:
  1468.                     i = line.index('endstream')
  1469.                     objlen += i
  1470.                     data += line[:i]
  1471.                     break
  1472.                 objlen += len(line)
  1473.                 data += line
  1474.             self.seek(pos+objlen)
  1475.             obj = PDFStream(dic, data, self.doc.decipher)
  1476.             self.push((pos, obj))
  1477.             return
  1478.        
  1479.         # others
  1480.         self.push((pos, token))
  1481.         return
  1482.  
  1483.     def find_xref(self):
  1484.         # search the last xref table by scanning the file backwards.
  1485.         prev = None
  1486.         for line in self.revreadlines():
  1487.             line = line.strip()
  1488.             if line == 'startxref': break
  1489.             if line:
  1490.                 prev = line
  1491.         else:
  1492.             raise PDFNoValidXRef('Unexpected EOF')
  1493.         return int(prev)
  1494.  
  1495.     # read xref table
  1496.     def read_xref_from(self, start, xrefs):
  1497.         self.seek(start)
  1498.         self.reset()
  1499.         try:
  1500.             (pos, token) = self.nexttoken()
  1501.         except PSEOF:
  1502.             raise PDFNoValidXRef('Unexpected EOF')
  1503.         if isinstance(token, int):
  1504.             # XRefStream: PDF-1.5
  1505.             if GEN_XREF_STM == 1:
  1506.                 global gen_xref_stm
  1507.                 gen_xref_stm = True
  1508.             self.seek(pos)
  1509.             self.reset()
  1510.             xref = PDFXRefStream()
  1511.             xref.load(self)
  1512.         else:
  1513.             if token is not self.KEYWORD_XREF:
  1514.                 raise PDFNoValidXRef('xref not found: pos=%d, token=%r' %
  1515.                                      (pos, token))
  1516.             self.nextline()
  1517.             xref = PDFXRef()
  1518.             xref.load(self)
  1519.         xrefs.append(xref)
  1520.         trailer = xref.trailer
  1521.         if 'XRefStm' in trailer:
  1522.             pos = int_value(trailer['XRefStm'])
  1523.             self.read_xref_from(pos, xrefs)
  1524.         if 'Prev' in trailer:
  1525.             # find previous xref
  1526.             pos = int_value(trailer['Prev'])
  1527.             self.read_xref_from(pos, xrefs)
  1528.         return
  1529.        
  1530.     # read xref tables and trailers
  1531.     def read_xref(self):
  1532.         xrefs = []
  1533.         trailerpos = None
  1534.         try:
  1535.             pos = self.find_xref()
  1536.             self.read_xref_from(pos, xrefs)
  1537.         except PDFNoValidXRef:
  1538.             # fallback
  1539.             self.seek(0)
  1540.             pat = re.compile(r'^(\d+)\s+(\d+)\s+obj\b')
  1541.             offsets = {}
  1542.             xref = PDFXRef()
  1543.             while 1:
  1544.                 try:
  1545.                     (pos, line) = self.nextline()
  1546.                 except PSEOF:
  1547.                     break
  1548.                 if line.startswith('trailer'):
  1549.                     trailerpos = pos # remember last trailer
  1550.                 m = pat.match(line)
  1551.                 if not m: continue
  1552.                 (objid, genno) = m.groups()
  1553.                 offsets[int(objid)] = (0, pos)
  1554.             if not offsets: raise
  1555.             xref.offsets = offsets
  1556.             if trailerpos:
  1557.                 self.seek(trailerpos)
  1558.                 xref.load_trailer(self)
  1559.                 xrefs.append(xref)
  1560.         return xrefs
  1561.  
  1562. ##  PDFObjStrmParser
  1563. ##
  1564. class PDFObjStrmParser(PDFParser):
  1565.  
  1566.     def __init__(self, data, doc):
  1567.         PSStackParser.__init__(self, StringIO(data))
  1568.         self.doc = doc
  1569.         return
  1570.  
  1571.     def flush(self):
  1572.         self.add_results(*self.popall())
  1573.         return
  1574.  
  1575.     KEYWORD_R = KWD('R')
  1576.     def do_keyword(self, pos, token):
  1577.         if token is self.KEYWORD_R:
  1578.             # reference to indirect object
  1579.             try:
  1580.                 ((_,objid), (_,genno)) = self.pop(2)
  1581.                 (objid, genno) = (int(objid), int(genno))
  1582.                 obj = PDFObjRef(self.doc, objid, genno)
  1583.                 self.push((pos, obj))
  1584.             except PSSyntaxError:
  1585.                 pass
  1586.             return
  1587.         # others
  1588.         self.push((pos, token))
  1589.         return
  1590.  
  1591. ###
  1592. ### My own code, for which there is none else to blame
  1593.  
  1594. class PDFSerializer(object):
  1595.     def __init__(self, inf, keypath):
  1596.         global GEN_XREF_STM, gen_xref_stm
  1597.         gen_xref_stm = GEN_XREF_STM > 1
  1598.         self.version = inf.read(8)
  1599.         inf.seek(0)
  1600.         self.doc = doc = PDFDocument()
  1601.         parser = PDFParser(doc, inf)
  1602.         doc.initialize(keypath)
  1603.         self.objids = objids = set()
  1604.         for xref in reversed(doc.xrefs):
  1605.             trailer = xref.trailer
  1606.             for objid in xref.objids():
  1607.                 objids.add(objid)
  1608.         trailer = dict(trailer)
  1609.         trailer.pop('Prev', None)
  1610.         trailer.pop('XRefStm', None)
  1611.         if 'Encrypt' in trailer:
  1612.             objids.remove(trailer.pop('Encrypt').objid)
  1613.         self.trailer = trailer
  1614.  
  1615.     def dump(self, outf):
  1616.         self.outf = outf
  1617.         self.write(self.version)
  1618.         self.write('\n%\xe2\xe3\xcf\xd3\n')
  1619.         doc = self.doc
  1620.         objids = self.objids
  1621.         xrefs = {}
  1622.         maxobj = max(objids)
  1623.         trailer = dict(self.trailer)
  1624.         trailer['Size'] = maxobj + 1
  1625.         for objid in objids:
  1626.             obj = doc.getobj(objid)
  1627.             if isinstance(obj, PDFObjStmRef):
  1628.                 xrefs[objid] = obj
  1629.                 continue
  1630.             if obj is not None:
  1631.                 try:
  1632.                     genno = obj.genno
  1633.                 except AttributeError:
  1634.                     genno = 0
  1635.                 xrefs[objid] = (self.tell(), genno)
  1636.                 self.serialize_indirect(objid, obj)
  1637.         startxref = self.tell()
  1638.  
  1639.         if not gen_xref_stm:
  1640.             self.write('xref\n')
  1641.             self.write('0 %d\n' % (maxobj + 1,))
  1642.             for objid in xrange(0, maxobj + 1):
  1643.                 if objid in xrefs:
  1644.                     # force the genno to be 0
  1645.                     self.write("%010d 00000 n \n" % xrefs[objid][0])
  1646.                 else:
  1647.                     self.write("%010d %05d f \n" % (0, 65535))
  1648.            
  1649.             self.write('trailer\n')
  1650.             self.serialize_object(trailer)
  1651.             self.write('\nstartxref\n%d\n%%%%EOF' % startxref)
  1652.  
  1653.         else: # Generate crossref stream.
  1654.  
  1655.             # Calculate size of entries
  1656.             maxoffset = max(startxref, maxobj)
  1657.             maxindex = PDFObjStmRef.maxindex
  1658.             fl2 = 2
  1659.             power = 65536
  1660.             while maxoffset >= power:
  1661.                 fl2 += 1
  1662.                 power *= 256
  1663.             fl3 = 1
  1664.             power = 256
  1665.             while maxindex >= power:
  1666.                 fl3 += 1
  1667.                 power *= 256
  1668.                    
  1669.             index = []
  1670.             first = None
  1671.             prev = None
  1672.             data = []
  1673.             # Put the xrefstream's reference in itself
  1674.             startxref = self.tell()
  1675.             maxobj += 1
  1676.             xrefs[maxobj] = (startxref, 0)
  1677.             for objid in sorted(xrefs):
  1678.                 if first is None:
  1679.                     first = objid
  1680.                 elif objid != prev + 1:
  1681.                     index.extend((first, prev - first + 1))
  1682.                     first = objid
  1683.                 prev = objid
  1684.                 objref = xrefs[objid]
  1685.                 if isinstance(objref, PDFObjStmRef):
  1686.                     f1 = 2
  1687.                     f2 = objref.stmid
  1688.                     f3 = objref.index
  1689.                 else:
  1690.                     f1 = 1
  1691.                     f2 = objref[0]
  1692.                     # we force all generation numbers to be 0
  1693.                     # f3 = objref[1]
  1694.                     f3 = 0
  1695.                
  1696.                 data.append(struct.pack('>B', f1))
  1697.                 data.append(struct.pack('>L', f2)[-fl2:])
  1698.                 data.append(struct.pack('>L', f3)[-fl3:])
  1699.             index.extend((first, prev - first + 1))
  1700.             data = zlib.compress(''.join(data))
  1701.             dic = {'Type': LITERAL_XREF, 'Size': prev + 1, 'Index': index,
  1702.                    'W': [1, fl2, fl3], 'Length': len(data),
  1703.                    'Filter': LITERALS_FLATE_DECODE[0],
  1704.                    'Root': trailer['Root'],}
  1705.             if 'Info' in trailer:
  1706.                 dic['Info'] = trailer['Info']
  1707.             xrefstm = PDFStream(dic, data)
  1708.             self.serialize_indirect(maxobj, xrefstm)
  1709.             self.write('startxref\n%d\n%%%%EOF' % startxref)
  1710.  
  1711.     def write(self, data):
  1712.         self.outf.write(data)
  1713.         self.last = data[-1:]
  1714.  
  1715.     def tell(self):
  1716.         return self.outf.tell()
  1717.  
  1718.     def escape_string(self, string):
  1719.         string = string.replace('\\', '\\\\')
  1720.         string = string.replace('\n', r'\n')
  1721.         string = string.replace('(', r'\(')
  1722.         string = string.replace(')', r'\)')
  1723.          # get rid of ciando id
  1724.         regularexp = re.compile(r'http://www.ciando.com/index.cfm/intRefererID/\d{5}')
  1725.         if regularexp.match(string): return ('http://www.ciando.com')
  1726.         return string
  1727.    
  1728.     def serialize_object(self, obj):
  1729.         if isinstance(obj, dict):
  1730.             # Correct malformed Mac OS resource forks for Stanza
  1731.             if 'ResFork' in obj and 'Type' in obj and 'Subtype' not in obj \
  1732.                    and isinstance(obj['Type'], int):
  1733.                 obj['Subtype'] = obj['Type']
  1734.                 del obj['Type']
  1735.             # end - hope this doesn't have bad effects
  1736.             self.write('<<')
  1737.             for key, val in obj.items():
  1738.                 self.write('/%s' % key)
  1739.                 self.serialize_object(val)
  1740.             self.write('>>')
  1741.         elif isinstance(obj, list):
  1742.             self.write('[')
  1743.             for val in obj:
  1744.                 self.serialize_object(val)
  1745.             self.write(']')
  1746.         elif isinstance(obj, str):
  1747.             self.write('(%s)' % self.escape_string(obj))
  1748.         elif isinstance(obj, bool):
  1749.             if self.last.isalnum():
  1750.                 self.write(' ')
  1751.             self.write(str(obj).lower())            
  1752.         elif isinstance(obj, (int, long, float)):
  1753.             if self.last.isalnum():
  1754.                 self.write(' ')
  1755.             self.write(str(obj))
  1756.         elif isinstance(obj, PDFObjRef):
  1757.             if self.last.isalnum():
  1758.                 self.write(' ')            
  1759.             self.write('%d %d R' % (obj.objid, 0))
  1760.         elif isinstance(obj, PDFStream):
  1761.             ### If we don't generate cross ref streams the object streams
  1762.             ### are no longer useful, as we have extracted all objects from
  1763.             ### them. Therefore leave them out from the output.
  1764.             if obj.dic.get('Type') == LITERAL_OBJSTM and not gen_xref_stm:
  1765.                     self.write('(deleted)')
  1766.             else:
  1767.                 data = obj.get_decdata()
  1768.                 self.serialize_object(obj.dic)
  1769.                 self.write('stream\n')
  1770.                 self.write(data)
  1771.                 self.write('\nendstream')
  1772.         else:
  1773.             data = str(obj)
  1774.             if data[0].isalnum() and self.last.isalnum():
  1775.                 self.write(' ')
  1776.             self.write(data)
  1777.    
  1778.     def serialize_indirect(self, objid, obj):
  1779.         self.write('%d 0 obj' % (objid,))
  1780.         self.serialize_object(obj)
  1781.         if self.last.isalnum():
  1782.             self.write('\n')
  1783.         self.write('endobj\n')
  1784.  
  1785. def cli_main(argv=sys.argv):
  1786.     progname = os.path.basename(argv[0])
  1787.     if RSA is None:
  1788.         print "%s: This script requires PyCrypto, which must be installed " \
  1789.               "separately.  Read the top-of-script comment for details." % \
  1790.               (progname,)
  1791.         return 1
  1792.     if len(argv) != 4:
  1793.         print "usage: %s KEYFILE INBOOK OUTBOOK" % (progname,)
  1794.         return 1
  1795.     keypath, inpath, outpath = argv[1:]
  1796.     with open(inpath, 'rb') as inf:
  1797.         serializer = PDFSerializer(inf, keypath)
  1798.         with open(outpath, 'wb') as outf:
  1799.             serializer.dump(outf)
  1800.     return 0
  1801.  
  1802.  
  1803. class DecryptionDialog(Tkinter.Frame):
  1804.     def __init__(self, root):
  1805.         Tkinter.Frame.__init__(self, root, border=5)
  1806.         self.status = Tkinter.Label(self, text='Automated Inept PDF V7.4 - Greetings to ILC!')
  1807.         self.status.pack(fill=Tkconstants.X, expand=1)
  1808.         body = Tkinter.Frame(self)
  1809.         body.pack(fill=Tkconstants.X, expand=1)
  1810.         sticky = Tkconstants.E + Tkconstants.W
  1811.         body.grid_columnconfigure(1, weight=2)
  1812.         Tkinter.Label(body, text='Key file').grid(row=0)
  1813.         self.keypath = Tkinter.Entry(body, width=35)
  1814.         self.keypath.grid(row=0, column=1, sticky=sticky)
  1815.         if os.path.exists('adeptkey.der'):
  1816.             self.keypath.insert(0, 'adeptkey.der')
  1817.         button = Tkinter.Button(body, text="...", command=self.get_keypath)
  1818.         button.grid(row=0, column=2)
  1819.         Tkinter.Label(body, text='Input Path').grid(row=1)
  1820.         self.inpath = Tkinter.Entry(body, width=35)
  1821.         self.inpath.grid(row=1, column=1, sticky=sticky)
  1822.         button = Tkinter.Button(body, text="...", command=self.get_inpath)
  1823.         button.grid(row=1, column=2)
  1824.         Tkinter.Label(body, text='Output Path').grid(row=2)
  1825.         self.outpath = Tkinter.Entry(body, width=35)
  1826.         self.outpath.grid(row=2, column=1, sticky=sticky)
  1827.         button = Tkinter.Button(body, text="...", command=self.get_outpath)
  1828.         button.grid(row=2, column=2)
  1829.         buttons = Tkinter.Frame(self)
  1830.         buttons.pack()
  1831.         botton = Tkinter.Button(
  1832.             buttons, text="Decrypt whole directory", width=35, command=self.decrypt)
  1833.         botton.pack(side=Tkconstants.LEFT)
  1834.         Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
  1835.         button = Tkinter.Button(
  1836.             buttons, text="Quit", width=10, command=self.quit)
  1837.         button.pack(side=Tkconstants.RIGHT)
  1838.  
  1839.     def get_keypath(self):
  1840.         keypath = tkFileDialog.askopenfilename(
  1841.             parent=None, title='Select ADEPT key file',
  1842.             defaultextension='.der', filetypes=[('DER-encoded files', '.der'),
  1843.                                                 ('All Files', '.*')])
  1844.         if keypath:
  1845.             keypath = os.path.normpath(keypath)
  1846.             self.keypath.delete(0, Tkconstants.END)
  1847.             self.keypath.insert(0, keypath)
  1848.         return
  1849.  
  1850.     def get_inpath(self):
  1851.         inpath = tkFileDialog.askdirectory(parent=None, title='Select encrypted PDF source directory')
  1852.            # defaultextension='.epub', filetypes=[('PDF files', '.pdf'),
  1853.             #                                     ('All files', '.*')])
  1854.         if inpath:
  1855.             inpath = os.path.normpath(inpath)
  1856.             self.inpath.delete(0, Tkconstants.END)
  1857.             self.inpath.insert(0, inpath)
  1858.  
  1859.         return
  1860.  
  1861.     def get_outpath(self):
  1862.         outpath = tkFileDialog.askdirectory(parent=None, title='Select directory to decrypt to')
  1863.         if outpath:
  1864.             outpath = os.path.normpath(outpath)
  1865.             self.outpath.delete(0, Tkconstants.END)
  1866.             self.outpath.insert(0, outpath)
  1867.         return
  1868.  
  1869.     def decrypt(self):
  1870.         keypath = self.keypath.get()
  1871.         inpath = self.inpath.get()
  1872.         outpath = self.outpath.get()
  1873.         if not keypath or not os.path.exists(keypath):
  1874.             self.status['text'] = 'Specified key file does not exist'
  1875.             return
  1876.         if not inpath or not os.path.exists(inpath):
  1877.             self.status['text'] = 'Specified input directory does not exist'
  1878.             return
  1879.         if not outpath:
  1880.             self.status['text'] = 'Output directory not specified'
  1881.             return
  1882.         if inpath == outpath:
  1883.             self.status['text'] = 'Must have different input and output directory'
  1884.             return
  1885.         dirlist = os.listdir(inpath)
  1886.         # get regular expression match for pdfs
  1887.         match = r'.pdf'
  1888.         # determine if I need a slash or backslash (Windows or Linux/Unix/Mac OS
  1889.         if re.match('//',inpath):
  1890.             inpath=inpath+'//'
  1891.             outpath=outpath+'//'
  1892.         else:
  1893.             inpath=inpath+'\\'
  1894.             outpath=outpath+'\\'
  1895.         for fname in dirlist:
  1896.              root, ext = os.path.splitext(fname)
  1897.              if re.match(match,ext,re.IGNORECASE):
  1898. ##                print keypath
  1899. ##                print inpath+"\\"+fname
  1900. ##                print outpath+"\\"+root+"_decrypted.pdf"
  1901.                 argv = [sys.argv[0], keypath, inpath+fname, outpath+root+".jb.decrypted.pdf"]
  1902.                 #                self.status['text'] = 'Decrypting...'
  1903.                 self.status['text'] = 'Decrypting:'+fname
  1904.                 try:
  1905.                     cli_main(argv)
  1906.                 except Exception, e:
  1907.                     self.status['text'] = 'Error in: '+ fname + str(e)
  1908.                     return
  1909.                 self.status['text'] = 'Decrypted directory '+inpath
  1910.  
  1911. def gui_main():
  1912.     root = Tkinter.Tk()
  1913.     if RSA is None:
  1914.         root.withdraw()
  1915.         tkMessageBox.showerror(
  1916.             "Automated INEPT PDF Decrypter",
  1917.             "This script requires PyCrypto, which must be installed "
  1918.             "separately.  Read the top-of-script comment for details.")
  1919.         return 1
  1920.     root.title('Automated INEPT PDF Decrypter')
  1921.     root.resizable(True, False)
  1922.     root.minsize(300, 0)
  1923.     DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
  1924.     root.mainloop()
  1925.     return 0
  1926.  
  1927.  
  1928. if __name__ == '__main__':
  1929.     if len(sys.argv) > 1:
  1930.         sys.exit(cli_main())
  1931.     sys.exit(gui_main())
Add Comment
Please, Sign In to add comment