Guest User

Untitled

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