Guest User

ineptpdf 8.4.2

a guest
Jun 23rd, 2010
752
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 82.50 KB | None | 0 0
  1. #! /usr/bin/python
  2.  
  3. # ineptpdf842.pyw
  4. # ineptpdf, version 8.4.2
  5.  
  6. # To run this program install Python 2.6 from http://www.python.org/download/
  7. # and PyCrypto from http://www.voidspace.org.uk/python/modules.shtml#pycrypto
  8. # (make sure to install the version for Python 2.6).
  9. #
  10. # It's recommended to use the 32-Bit Python Windows version (even with a 64-bit
  11. # Windows system).
  12. #
  13. # Save this script file as
  14. # ineptpdf8.4.2.pyw and double-click on it to run it.
  15.  
  16. # Revision history:
  17. #   1 - Initial release
  18. #   2 - Improved determination of key-generation algorithm
  19. #   3 - Correctly handle PDF >=1.5 cross-reference streams
  20. #   4 - Removal of ciando's personal ID (anon)
  21. #   5 - removing small bug with V3 ebooks (anon)
  22. #   6 - changed to adeptkey4.der format for 1.7.2 support (anon)
  23. #   6.1 - backward compatibility for 1.7.1 and old adeptkey.der
  24. #   7 - Get cross reference streams and object streams working for input.
  25. #       Not yet supported on output but this only effects file size,
  26. #       not functionality. (by anon2)
  27. #   7.1 - Correct a problem when an old trailer is not followed by startxref
  28. #   7.2 - Correct malformed Mac OS resource forks for Stanza
  29. #       - Support for cross ref streams on output (decreases file size)
  30. #   7.3 - Correct bug in trailer with cross ref stream that caused the error
  31. #         "The root object is missing or invalid" in Adobe Reader.
  32. #   7.4 - Force all generation numbers in output file to be 0, like in v6.
  33. #         Fallback code for wrong xref improved (search till last trailer
  34. #         instead of first)
  35. #   8 - fileopen user machine identifier support (Tetrachroma)
  36. #   8.1 - fileopen user cookies support (Tetrachroma)
  37. #   8.2 - fileopen user name/password support (Tetrachroma)
  38. #   8.3 - fileopen session cookie support
  39. #   8.3.1 - fix for the "specified key file does not exist" error (Tetrachroma)
  40. #   8.3.2 - improved server result parsing (Tetrachroma)
  41. #   8.4 - Ident4D and encrypted Uuid support (Tetrachroma)
  42. #   8.4.1 - improved MAC address processing (Tetrachroma)
  43. #   8.4.2 - FowP3Uuid fallback file processing
  44.  
  45. """
  46. Decrypts Adobe ADEPT-encrypted and Fileopen PDF files.
  47. """
  48.  
  49. from __future__ import with_statement
  50.  
  51. __license__ = 'GPL v3'
  52.  
  53. import sys
  54. import os
  55. import re
  56. import zlib
  57. import struct
  58. import hashlib
  59. from itertools import chain, islice
  60. import xml.etree.ElementTree as etree
  61. import Tkinter
  62. import Tkconstants
  63. import tkFileDialog
  64. import tkMessageBox
  65. # added for fileopen support
  66. import urllib
  67. import urllib2
  68. import urlparse
  69. import time
  70. import socket
  71. import string
  72. import uuid
  73. import subprocess
  74. import time
  75. import getpass
  76. from ctypes import *
  77.  
  78. try:
  79.     from Crypto.Cipher import ARC4
  80.     from Crypto.PublicKey import RSA
  81. except ImportError:
  82.     ARC4 = None
  83.     RSA = None
  84. try:
  85.     from cStringIO import StringIO
  86. except ImportError:
  87.     from StringIO import StringIO
  88.  
  89.  
  90. class ADEPTError(Exception):
  91.     pass
  92.  
  93. # global variable (needed for fileopen)
  94. INPUTFILEPATH = ''
  95. KEYFILEPATH = ''
  96. # Do we generate cross reference streams on output?
  97. # 0 = never
  98. # 1 = only if present in input
  99. # 2 = always
  100.  
  101. GEN_XREF_STM = 1
  102.  
  103. # This is the value for the current document
  104. gen_xref_stm = False # will be set in PDFSerializer
  105.  
  106. ###
  107. ### ASN.1 parsing code from tlslite
  108.  
  109. def bytesToNumber(bytes):
  110.     total = 0L
  111.     for byte in bytes:
  112.         total = (total << 8) + byte
  113.     return total
  114.  
  115. class ASN1Error(Exception):
  116.     pass
  117.  
  118. class ASN1Parser(object):
  119.     class Parser(object):
  120.         def __init__(self, bytes):
  121.             self.bytes = bytes
  122.             self.index = 0
  123.    
  124.         def get(self, length):
  125.             if self.index + length > len(self.bytes):
  126.                 raise ASN1Error("Error decoding ASN.1")
  127.             x = 0
  128.             for count in range(length):
  129.                 x <<= 8
  130.                 x |= self.bytes[self.index]
  131.                 self.index += 1
  132.             return x
  133.    
  134.         def getFixBytes(self, lengthBytes):
  135.             bytes = self.bytes[self.index : self.index+lengthBytes]
  136.             self.index += lengthBytes
  137.             return bytes
  138.    
  139.         def getVarBytes(self, lengthLength):
  140.             lengthBytes = self.get(lengthLength)
  141.             return self.getFixBytes(lengthBytes)
  142.    
  143.         def getFixList(self, length, lengthList):
  144.             l = [0] * lengthList
  145.             for x in range(lengthList):
  146.                 l[x] = self.get(length)
  147.             return l
  148.    
  149.         def getVarList(self, length, lengthLength):
  150.             lengthList = self.get(lengthLength)
  151.             if lengthList % length != 0:
  152.                 raise ASN1Error("Error decoding ASN.1")
  153.             lengthList = int(lengthList/length)
  154.             l = [0] * lengthList
  155.             for x in range(lengthList):
  156.                 l[x] = self.get(length)
  157.             return l
  158.    
  159.         def startLengthCheck(self, lengthLength):
  160.             self.lengthCheck = self.get(lengthLength)
  161.             self.indexCheck = self.index
  162.    
  163.         def setLengthCheck(self, length):
  164.             self.lengthCheck = length
  165.             self.indexCheck = self.index
  166.    
  167.         def stopLengthCheck(self):
  168.             if (self.index - self.indexCheck) != self.lengthCheck:
  169.                 raise ASN1Error("Error decoding ASN.1")
  170.    
  171.         def atLengthCheck(self):
  172.             if (self.index - self.indexCheck) < self.lengthCheck:
  173.                 return False
  174.             elif (self.index - self.indexCheck) == self.lengthCheck:
  175.                 return True
  176.             else:
  177.                 raise ASN1Error("Error decoding ASN.1")
  178.  
  179.     def __init__(self, bytes):
  180.         p = self.Parser(bytes)
  181.         p.get(1)
  182.         self.length = self._getASN1Length(p)
  183.         self.value = p.getFixBytes(self.length)
  184.  
  185.     def getChild(self, which):
  186.         p = self.Parser(self.value)
  187.         for x in range(which+1):
  188.             markIndex = p.index
  189.             p.get(1)
  190.             length = self._getASN1Length(p)
  191.             p.getFixBytes(length)
  192.         return ASN1Parser(p.bytes[markIndex:p.index])
  193.  
  194.     def _getASN1Length(self, p):
  195.         firstLength = p.get(1)
  196.         if firstLength<=127:
  197.             return firstLength
  198.         else:
  199.             lengthLength = firstLength & 0x7F
  200.             return p.get(lengthLength)
  201.  
  202. ###
  203. ### PDF parsing routines from pdfminer, with changes for EBX_HANDLER
  204.  
  205. ##  Utilities
  206. ##
  207. def choplist(n, seq):
  208.     '''Groups every n elements of the list.'''
  209.     r = []
  210.     for x in seq:
  211.         r.append(x)
  212.         if len(r) == n:
  213.             yield tuple(r)
  214.             r = []
  215.     return
  216.  
  217. def nunpack(s, default=0):
  218.     '''Unpacks up to 4 bytes big endian.'''
  219.     l = len(s)
  220.     if not l:
  221.         return default
  222.     elif l == 1:
  223.         return ord(s)
  224.     elif l == 2:
  225.         return struct.unpack('>H', s)[0]
  226.     elif l == 3:
  227.         return struct.unpack('>L', '\x00'+s)[0]
  228.     elif l == 4:
  229.         return struct.unpack('>L', s)[0]
  230.     else:
  231.         return TypeError('invalid length: %d' % l)
  232.  
  233.  
  234. STRICT = 1
  235.  
  236.  
  237. ##  PS Exceptions
  238. ##
  239. class PSException(Exception): pass
  240. class PSEOF(PSException): pass
  241. class PSSyntaxError(PSException): pass
  242. class PSTypeError(PSException): pass
  243. class PSValueError(PSException): pass
  244.  
  245.  
  246. ##  Basic PostScript Types
  247. ##
  248.  
  249. # PSLiteral
  250. class PSObject(object): pass
  251.  
  252. class PSLiteral(PSObject):
  253.     '''
  254.    PS literals (e.g. "/Name").
  255.    Caution: Never create these objects directly.
  256.    Use PSLiteralTable.intern() instead.
  257.    '''
  258.     def __init__(self, name):
  259.         self.name = name
  260.         return
  261.    
  262.     def __repr__(self):
  263.         name = []
  264.         for char in self.name:
  265.             if not char.isalnum():
  266.                 char = '#%02x' % ord(char)
  267.             name.append(char)
  268.         return '/%s' % ''.join(name)
  269.  
  270. # PSKeyword
  271. class PSKeyword(PSObject):
  272.     '''
  273.    PS keywords (e.g. "showpage").
  274.    Caution: Never create these objects directly.
  275.    Use PSKeywordTable.intern() instead.
  276.    '''
  277.     def __init__(self, name):
  278.         self.name = name
  279.         return
  280.    
  281.     def __repr__(self):
  282.         return self.name
  283.  
  284. # PSSymbolTable
  285. class PSSymbolTable(object):
  286.    
  287.     '''
  288.    Symbol table that stores PSLiteral or PSKeyword.
  289.    '''
  290.    
  291.     def __init__(self, classe):
  292.         self.dic = {}
  293.         self.classe = classe
  294.         return
  295.    
  296.     def intern(self, name):
  297.         if name in self.dic:
  298.             lit = self.dic[name]
  299.         else:
  300.             lit = self.classe(name)
  301.             self.dic[name] = lit
  302.         return lit
  303.  
  304. PSLiteralTable = PSSymbolTable(PSLiteral)
  305. PSKeywordTable = PSSymbolTable(PSKeyword)
  306. LIT = PSLiteralTable.intern
  307. KWD = PSKeywordTable.intern
  308. KEYWORD_BRACE_BEGIN = KWD('{')
  309. KEYWORD_BRACE_END = KWD('}')
  310. KEYWORD_ARRAY_BEGIN = KWD('[')
  311. KEYWORD_ARRAY_END = KWD(']')
  312. KEYWORD_DICT_BEGIN = KWD('<<')
  313. KEYWORD_DICT_END = KWD('>>')
  314.  
  315.  
  316. def literal_name(x):
  317.     if not isinstance(x, PSLiteral):
  318.         if STRICT:
  319.             raise PSTypeError('Literal required: %r' % x)
  320.         else:
  321.             return str(x)
  322.     return x.name
  323.  
  324. def keyword_name(x):
  325.     if not isinstance(x, PSKeyword):
  326.         if STRICT:
  327.             raise PSTypeError('Keyword required: %r' % x)
  328.         else:
  329.             return str(x)
  330.     return x.name
  331.  
  332.  
  333. ##  PSBaseParser
  334. ##
  335. EOL = re.compile(r'[\r\n]')
  336. SPC = re.compile(r'\s')
  337. NONSPC = re.compile(r'\S')
  338. HEX = re.compile(r'[0-9a-fA-F]')
  339. END_LITERAL = re.compile(r'[#/%\[\]()<>{}\s]')
  340. END_HEX_STRING = re.compile(r'[^\s0-9a-fA-F]')
  341. HEX_PAIR = re.compile(r'[0-9a-fA-F]{2}|.')
  342. END_NUMBER = re.compile(r'[^0-9]')
  343. END_KEYWORD = re.compile(r'[#/%\[\]()<>{}\s]')
  344. END_STRING = re.compile(r'[()\134]')
  345. OCT_STRING = re.compile(r'[0-7]')
  346. ESC_STRING = { 'b':8, 't':9, 'n':10, 'f':12, 'r':13, '(':40, ')':41, '\\':92 }
  347.  
  348. class PSBaseParser(object):
  349.  
  350.     '''
  351.    Most basic PostScript parser that performs only basic tokenization.
  352.    '''
  353.     BUFSIZ = 4096
  354.  
  355.     def __init__(self, fp):
  356.         self.fp = fp
  357.         self.seek(0)
  358.         return
  359.  
  360.     def __repr__(self):
  361.         return '<PSBaseParser: %r, bufpos=%d>' % (self.fp, self.bufpos)
  362.  
  363.     def flush(self):
  364.         return
  365.    
  366.     def close(self):
  367.         self.flush()
  368.         return
  369.    
  370.     def tell(self):
  371.         return self.bufpos+self.charpos
  372.  
  373.     def poll(self, pos=None, n=80):
  374.         pos0 = self.fp.tell()
  375.         if not pos:
  376.             pos = self.bufpos+self.charpos
  377.         self.fp.seek(pos)
  378.         print >>sys.stderr, 'poll(%d): %r' % (pos, self.fp.read(n))
  379.         self.fp.seek(pos0)
  380.         return
  381.  
  382.     def seek(self, pos):
  383.         '''
  384.        Seeks the parser to the given position.
  385.        '''
  386.         self.fp.seek(pos)
  387.         # reset the status for nextline()
  388.         self.bufpos = pos
  389.         self.buf = ''
  390.         self.charpos = 0
  391.         # reset the status for nexttoken()
  392.         self.parse1 = self.parse_main
  393.         self.tokens = []
  394.         return
  395.  
  396.     def fillbuf(self):
  397.         if self.charpos < len(self.buf): return
  398.         # fetch next chunk.
  399.         self.bufpos = self.fp.tell()
  400.         self.buf = self.fp.read(self.BUFSIZ)
  401.         if not self.buf:
  402.             raise PSEOF('Unexpected EOF')
  403.         self.charpos = 0
  404.         return
  405.    
  406.     def parse_main(self, s, i):
  407.         m = NONSPC.search(s, i)
  408.         if not m:
  409.             return (self.parse_main, len(s))
  410.         j = m.start(0)
  411.         c = s[j]
  412.         self.tokenstart = self.bufpos+j
  413.         if c == '%':
  414.             self.token = '%'
  415.             return (self.parse_comment, j+1)
  416.         if c == '/':
  417.             self.token = ''
  418.             return (self.parse_literal, j+1)
  419.         if c in '-+' or c.isdigit():
  420.             self.token = c
  421.             return (self.parse_number, j+1)
  422.         if c == '.':
  423.             self.token = c
  424.             return (self.parse_float, j+1)
  425.         if c.isalpha():
  426.             self.token = c
  427.             return (self.parse_keyword, j+1)
  428.         if c == '(':
  429.             self.token = ''
  430.             self.paren = 1
  431.             return (self.parse_string, j+1)
  432.         if c == '<':
  433.             self.token = ''
  434.             return (self.parse_wopen, j+1)
  435.         if c == '>':
  436.             self.token = ''
  437.             return (self.parse_wclose, j+1)
  438.         self.add_token(KWD(c))
  439.         return (self.parse_main, j+1)
  440.                            
  441.     def add_token(self, obj):
  442.         self.tokens.append((self.tokenstart, obj))
  443.         return
  444.    
  445.     def parse_comment(self, s, i):
  446.         m = EOL.search(s, i)
  447.         if not m:
  448.             self.token += s[i:]
  449.             return (self.parse_comment, len(s))
  450.         j = m.start(0)
  451.         self.token += s[i:j]
  452.         # We ignore comments.
  453.         #self.tokens.append(self.token)
  454.         return (self.parse_main, j)
  455.    
  456.     def parse_literal(self, s, i):
  457.         m = END_LITERAL.search(s, i)
  458.         if not m:
  459.             self.token += s[i:]
  460.             return (self.parse_literal, len(s))
  461.         j = m.start(0)
  462.         self.token += s[i:j]
  463.         c = s[j]
  464.         if c == '#':
  465.             self.hex = ''
  466.             return (self.parse_literal_hex, j+1)
  467.         self.add_token(LIT(self.token))
  468.         return (self.parse_main, j)
  469.    
  470.     def parse_literal_hex(self, s, i):
  471.         c = s[i]
  472.         if HEX.match(c) and len(self.hex) < 2:
  473.             self.hex += c
  474.             return (self.parse_literal_hex, i+1)
  475.         if self.hex:
  476.             self.token += chr(int(self.hex, 16))
  477.         return (self.parse_literal, i)
  478.  
  479.     def parse_number(self, s, i):
  480.         m = END_NUMBER.search(s, i)
  481.         if not m:
  482.             self.token += s[i:]
  483.             return (self.parse_number, len(s))
  484.         j = m.start(0)
  485.         self.token += s[i:j]
  486.         c = s[j]
  487.         if c == '.':
  488.             self.token += c
  489.             return (self.parse_float, j+1)
  490.         try:
  491.             self.add_token(int(self.token))
  492.         except ValueError:
  493.             pass
  494.         return (self.parse_main, j)
  495.     def parse_float(self, s, i):
  496.         m = END_NUMBER.search(s, i)
  497.         if not m:
  498.             self.token += s[i:]
  499.             return (self.parse_float, len(s))
  500.         j = m.start(0)
  501.         self.token += s[i:j]
  502.         self.add_token(float(self.token))
  503.         return (self.parse_main, j)
  504.    
  505.     def parse_keyword(self, s, i):
  506.         m = END_KEYWORD.search(s, i)
  507.         if not m:
  508.             self.token += s[i:]
  509.             return (self.parse_keyword, len(s))
  510.         j = m.start(0)
  511.         self.token += s[i:j]
  512.         if self.token == 'true':
  513.             token = True
  514.         elif self.token == 'false':
  515.             token = False
  516.         else:
  517.             token = KWD(self.token)
  518.         self.add_token(token)
  519.         return (self.parse_main, j)
  520.  
  521.     def parse_string(self, s, i):
  522.         m = END_STRING.search(s, i)
  523.         if not m:
  524.             self.token += s[i:]
  525.             return (self.parse_string, len(s))
  526.         j = m.start(0)
  527.         self.token += s[i:j]
  528.         c = s[j]
  529.         if c == '\\':
  530.             self.oct = ''
  531.             return (self.parse_string_1, j+1)
  532.         if c == '(':
  533.             self.paren += 1
  534.             self.token += c
  535.             return (self.parse_string, j+1)
  536.         if c == ')':
  537.             self.paren -= 1
  538.             if self.paren:
  539.                 self.token += c
  540.                 return (self.parse_string, j+1)
  541.         self.add_token(self.token)
  542.         return (self.parse_main, j+1)
  543.     def parse_string_1(self, s, i):
  544.         c = s[i]
  545.         if OCT_STRING.match(c) and len(self.oct) < 3:
  546.             self.oct += c
  547.             return (self.parse_string_1, i+1)
  548.         if self.oct:
  549.             self.token += chr(int(self.oct, 8))
  550.             return (self.parse_string, i)
  551.         if c in ESC_STRING:
  552.             self.token += chr(ESC_STRING[c])
  553.         return (self.parse_string, i+1)
  554.  
  555.     def parse_wopen(self, s, i):
  556.         c = s[i]
  557.         if c.isspace() or HEX.match(c):
  558.             return (self.parse_hexstring, i)
  559.         if c == '<':
  560.             self.add_token(KEYWORD_DICT_BEGIN)
  561.             i += 1
  562.         return (self.parse_main, i)
  563.  
  564.     def parse_wclose(self, s, i):
  565.         c = s[i]
  566.         if c == '>':
  567.             self.add_token(KEYWORD_DICT_END)
  568.             i += 1
  569.         return (self.parse_main, i)
  570.  
  571.     def parse_hexstring(self, s, i):
  572.         m = END_HEX_STRING.search(s, i)
  573.         if not m:
  574.             self.token += s[i:]
  575.             return (self.parse_hexstring, len(s))
  576.         j = m.start(0)
  577.         self.token += s[i:j]
  578.         token = HEX_PAIR.sub(lambda m: chr(int(m.group(0), 16)),
  579.                                                  SPC.sub('', self.token))
  580.         self.add_token(token)
  581.         return (self.parse_main, j)
  582.  
  583.     def nexttoken(self):
  584.         while not self.tokens:
  585.             self.fillbuf()
  586.             (self.parse1, self.charpos) = self.parse1(self.buf, self.charpos)
  587.         token = self.tokens.pop(0)
  588.         return token
  589.  
  590.     def nextline(self):
  591.         '''
  592.        Fetches a next line that ends either with \\r or \\n.
  593.        '''
  594.         linebuf = ''
  595.         linepos = self.bufpos + self.charpos
  596.         eol = False
  597.         while 1:
  598.             self.fillbuf()
  599.             if eol:
  600.                 c = self.buf[self.charpos]
  601.                 # handle '\r\n'
  602.                 if c == '\n':
  603.                     linebuf += c
  604.                     self.charpos += 1
  605.                 break
  606.             m = EOL.search(self.buf, self.charpos)
  607.             if m:
  608.                 linebuf += self.buf[self.charpos:m.end(0)]
  609.                 self.charpos = m.end(0)
  610.                 if linebuf[-1] == '\r':
  611.                     eol = True
  612.                 else:
  613.                     break
  614.             else:
  615.                 linebuf += self.buf[self.charpos:]
  616.                 self.charpos = len(self.buf)
  617.         return (linepos, linebuf)
  618.  
  619.     def revreadlines(self):
  620.         '''
  621.        Fetches a next line backword. This is used to locate
  622.        the trailers at the end of a file.
  623.        '''
  624.         self.fp.seek(0, 2)
  625.         pos = self.fp.tell()
  626.         buf = ''
  627.         while 0 < pos:
  628.             prevpos = pos
  629.             pos = max(0, pos-self.BUFSIZ)
  630.             self.fp.seek(pos)
  631.             s = self.fp.read(prevpos-pos)
  632.             if not s: break
  633.             while 1:
  634.                 n = max(s.rfind('\r'), s.rfind('\n'))
  635.                 if n == -1:
  636.                     buf = s + buf
  637.                     break
  638.                 yield s[n:]+buf
  639.                 s = s[:n]
  640.                 buf = ''
  641.         return
  642.  
  643.  
  644. ##  PSStackParser
  645. ##
  646. class PSStackParser(PSBaseParser):
  647.  
  648.     def __init__(self, fp):
  649.         PSBaseParser.__init__(self, fp)
  650.         self.reset()
  651.         return
  652.    
  653.     def reset(self):
  654.         self.context = []
  655.         self.curtype = None
  656.         self.curstack = []
  657.         self.results = []
  658.         return
  659.  
  660.     def seek(self, pos):
  661.         PSBaseParser.seek(self, pos)
  662.         self.reset()
  663.         return
  664.  
  665.     def push(self, *objs):
  666.         self.curstack.extend(objs)
  667.         return
  668.     def pop(self, n):
  669.         objs = self.curstack[-n:]
  670.         self.curstack[-n:] = []
  671.         return objs
  672.     def popall(self):
  673.         objs = self.curstack
  674.         self.curstack = []
  675.         return objs
  676.     def add_results(self, *objs):
  677.         self.results.extend(objs)
  678.         return
  679.  
  680.     def start_type(self, pos, type):
  681.         self.context.append((pos, self.curtype, self.curstack))
  682.         (self.curtype, self.curstack) = (type, [])
  683.         return
  684.     def end_type(self, type):
  685.         if self.curtype != type:
  686.             raise PSTypeError('Type mismatch: %r != %r' % (self.curtype, type))
  687.         objs = [ obj for (_,obj) in self.curstack ]
  688.         (pos, self.curtype, self.curstack) = self.context.pop()
  689.         return (pos, objs)
  690.  
  691.     def do_keyword(self, pos, token):
  692.         return
  693.    
  694.     def nextobject(self, direct=False):
  695.         '''
  696.        Yields a list of objects: keywords, literals, strings,
  697.        numbers, arrays and dictionaries. Arrays and dictionaries
  698.        are represented as Python sequence and dictionaries.
  699.        '''
  700.         while not self.results:
  701.             (pos, token) = self.nexttoken()
  702.             #print (pos,token), (self.curtype, self.curstack)
  703.             if (isinstance(token, int) or
  704.                     isinstance(token, float) or
  705.                     isinstance(token, bool) or
  706.                     isinstance(token, str) or
  707.                     isinstance(token, PSLiteral)):
  708.                 # normal token
  709.                 self.push((pos, token))
  710.             elif token == KEYWORD_ARRAY_BEGIN:
  711.                 # begin array
  712.                 self.start_type(pos, 'a')
  713.             elif token == KEYWORD_ARRAY_END:
  714.                 # end array
  715.                 try:
  716.                     self.push(self.end_type('a'))
  717.                 except PSTypeError:
  718.                     if STRICT: raise
  719.             elif token == KEYWORD_DICT_BEGIN:
  720.                 # begin dictionary
  721.                 self.start_type(pos, 'd')
  722.             elif token == KEYWORD_DICT_END:
  723.                 # end dictionary
  724.                 try:
  725.                     (pos, objs) = self.end_type('d')
  726.                     if len(objs) % 2 != 0:
  727.                         raise PSSyntaxError(
  728.                             'Invalid dictionary construct: %r' % objs)
  729.                     d = dict((literal_name(k), v) \
  730.                                  for (k,v) in choplist(2, objs))
  731.                     self.push((pos, d))
  732.                 except PSTypeError:
  733.                     if STRICT: raise
  734.             else:
  735.                 self.do_keyword(pos, token)
  736.             if self.context:
  737.                 continue
  738.             else:
  739.                 if direct:
  740.                     return self.pop(1)[0]
  741.                 self.flush()
  742.         obj = self.results.pop(0)
  743.         return obj
  744.  
  745.  
  746. LITERAL_CRYPT = PSLiteralTable.intern('Crypt')
  747. LITERALS_FLATE_DECODE = (PSLiteralTable.intern('FlateDecode'), PSLiteralTable.intern('Fl'))
  748. LITERALS_LZW_DECODE = (PSLiteralTable.intern('LZWDecode'), PSLiteralTable.intern('LZW'))
  749. LITERALS_ASCII85_DECODE = (PSLiteralTable.intern('ASCII85Decode'), PSLiteralTable.intern('A85'))
  750.  
  751.  
  752. ##  PDF Objects
  753. ##
  754. class PDFObject(PSObject): pass
  755.  
  756. class PDFException(PSException): pass
  757. class PDFTypeError(PDFException): pass
  758. class PDFValueError(PDFException): pass
  759. class PDFNotImplementedError(PSException): pass
  760.  
  761.  
  762. ##  PDFObjRef
  763. ##
  764. class PDFObjRef(PDFObject):
  765.    
  766.     def __init__(self, doc, objid, genno):
  767.         if objid == 0:
  768.             if STRICT:
  769.                 raise PDFValueError('PDF object id cannot be 0.')
  770.         self.doc = doc
  771.         self.objid = objid
  772.         self.genno = genno
  773.         return
  774.  
  775.     def __repr__(self):
  776.         return '<PDFObjRef:%d %d>' % (self.objid, self.genno)
  777.  
  778.     def resolve(self):
  779.         return self.doc.getobj(self.objid)
  780.  
  781.  
  782. # resolve
  783. def resolve1(x):
  784.     '''
  785.    Resolve an object. If this is an array or dictionary,
  786.    it may still contains some indirect objects inside.
  787.    '''
  788.     while isinstance(x, PDFObjRef):
  789.         x = x.resolve()
  790.     return x
  791.  
  792. def resolve_all(x):
  793.     '''
  794.    Recursively resolve X and all the internals.
  795.    Make sure there is no indirect reference within the nested object.
  796.    This procedure might be slow.
  797.    '''
  798.     while isinstance(x, PDFObjRef):
  799.         x = x.resolve()
  800.     if isinstance(x, list):
  801.         x = [ resolve_all(v) for v in x ]
  802.     elif isinstance(x, dict):
  803.         for (k,v) in x.iteritems():
  804.             x[k] = resolve_all(v)
  805.     return x
  806.  
  807. def decipher_all(decipher, objid, genno, x):
  808.     '''
  809.    Recursively decipher X.
  810.    '''
  811.     if isinstance(x, str):
  812.         return decipher(objid, genno, x)
  813.     decf = lambda v: decipher_all(decipher, objid, genno, v)
  814.     if isinstance(x, list):
  815.         x = [decf(v) for v in x]
  816.     elif isinstance(x, dict):
  817.         x = dict((k, decf(v)) for (k, v) in x.iteritems())
  818.     return x
  819.  
  820. # Type cheking
  821. def int_value(x):
  822.     x = resolve1(x)
  823.     if not isinstance(x, int):
  824.         if STRICT:
  825.             raise PDFTypeError('Integer required: %r' % x)
  826.         return 0
  827.     return x
  828.  
  829. def float_value(x):
  830.     x = resolve1(x)
  831.     if not isinstance(x, float):
  832.         if STRICT:
  833.             raise PDFTypeError('Float required: %r' % x)
  834.         return 0.0
  835.     return x
  836.  
  837. def num_value(x):
  838.     x = resolve1(x)
  839.     if not (isinstance(x, int) or isinstance(x, float)):
  840.         if STRICT:
  841.             raise PDFTypeError('Int or Float required: %r' % x)
  842.         return 0
  843.     return x
  844.  
  845. def str_value(x):
  846.     x = resolve1(x)
  847.     if not isinstance(x, str):
  848.         if STRICT:
  849.             raise PDFTypeError('String required: %r' % x)
  850.         return ''
  851.     return x
  852.  
  853. def list_value(x):
  854.     x = resolve1(x)
  855.     if not (isinstance(x, list) or isinstance(x, tuple)):
  856.         if STRICT:
  857.             raise PDFTypeError('List required: %r' % x)
  858.         return []
  859.     return x
  860.  
  861. def dict_value(x):
  862.     x = resolve1(x)
  863.     if not isinstance(x, dict):
  864.         if STRICT:
  865.             raise PDFTypeError('Dict required: %r' % x)
  866.         return {}
  867.     return x
  868.  
  869. def stream_value(x):
  870.     x = resolve1(x)
  871.     if not isinstance(x, PDFStream):
  872.         if STRICT:
  873.             raise PDFTypeError('PDFStream required: %r' % x)
  874.         return PDFStream({}, '')
  875.     return x
  876.  
  877. # ascii85decode(data)
  878. def ascii85decode(data):
  879.   n = b = 0
  880.   out = ''
  881.   for c in data:
  882.     if '!' <= c and c <= 'u':
  883.       n += 1
  884.       b = b*85+(ord(c)-33)
  885.       if n == 5:
  886.         out += struct.pack('>L',b)
  887.         n = b = 0
  888.     elif c == 'z':
  889.       assert n == 0
  890.       out += '\0\0\0\0'
  891.     elif c == '~':
  892.       if n:
  893.         for _ in range(5-n):
  894.           b = b*85+84
  895.         out += struct.pack('>L',b)[:n-1]
  896.       break
  897.   return out
  898.  
  899.  
  900. ##  PDFStream type
  901. ##
  902. class PDFStream(PDFObject):
  903.    
  904.     def __init__(self, dic, rawdata, decipher=None):
  905.         length = int_value(dic.get('Length', 0))
  906.         eol = rawdata[length:]
  907.         if eol in ('\r', '\n', '\r\n'):
  908.             rawdata = rawdata[:length]
  909.         if length != len(rawdata):
  910.             print >>sys.stderr, "[warning] data length mismatch"
  911.         self.dic = dic
  912.         self.rawdata = rawdata
  913.         self.decipher = decipher
  914.         self.data = None
  915.         self.decdata = None
  916.         self.objid = None
  917.         self.genno = None
  918.         return
  919.  
  920.     def set_objid(self, objid, genno):
  921.         self.objid = objid
  922.         self.genno = genno
  923.         return
  924.    
  925.     def __repr__(self):
  926.         if self.rawdata:
  927.             return '<PDFStream(%r): raw=%d, %r>' % \
  928.                    (self.objid, len(self.rawdata), self.dic)
  929.         else:
  930.             return '<PDFStream(%r): data=%d, %r>' % \
  931.                    (self.objid, len(self.data), self.dic)
  932.  
  933.     def decode(self):
  934.         assert self.data is None and self.rawdata is not None
  935.         data = self.rawdata
  936.         if self.decipher:
  937.             # Handle encryption
  938.             data = self.decipher(self.objid, self.genno, data)
  939.             if gen_xref_stm:
  940.                 self.decdata = data # keep decrypted data
  941.         if 'Filter' not in self.dic:
  942.             self.data = data
  943.             self.rawdata = None
  944.             print self.dict
  945.             return
  946.         filters = self.dic['Filter']
  947.         if not isinstance(filters, list):
  948.             filters = [ filters ]
  949.         for f in filters:
  950.             if f in LITERALS_FLATE_DECODE:
  951.                 # will get errors if the document is encrypted.
  952.                 data = zlib.decompress(data)
  953.             elif f in LITERALS_LZW_DECODE:
  954.                 data = ''.join(LZWDecoder(StringIO(data)).run())
  955.             elif f in LITERALS_ASCII85_DECODE:
  956.                 data = ascii85decode(data)
  957.             elif f == LITERAL_CRYPT:
  958.                 raise PDFNotImplementedError('/Crypt filter is unsupported')
  959.             else:
  960.                 raise PDFNotImplementedError('Unsupported filter: %r' % f)
  961.             # apply predictors
  962.             if 'DP' in self.dic:
  963.                 params = self.dic['DP']
  964.             else:
  965.                 params = self.dic.get('DecodeParms', {})
  966.             if 'Predictor' in params:
  967.                 pred = int_value(params['Predictor'])
  968.                 if pred:
  969.                     if pred != 12:
  970.                         raise PDFNotImplementedError(
  971.                             'Unsupported predictor: %r' % pred)
  972.                     if 'Columns' not in params:
  973.                         raise PDFValueError(
  974.                             'Columns undefined for predictor=12')
  975.                     columns = int_value(params['Columns'])
  976.                     buf = ''
  977.                     ent0 = '\x00' * columns
  978.                     for i in xrange(0, len(data), columns+1):
  979.                         pred = data[i]
  980.                         ent1 = data[i+1:i+1+columns]
  981.                         if pred == '\x02':
  982.                             ent1 = ''.join(chr((ord(a)+ord(b)) & 255) \
  983.                                                for (a,b) in zip(ent0,ent1))
  984.                         buf += ent1
  985.                         ent0 = ent1
  986.                     data = buf
  987.         self.data = data
  988.         self.rawdata = None
  989.         return
  990.  
  991.     def get_data(self):
  992.         if self.data is None:
  993.             self.decode()
  994.         return self.data
  995.  
  996.     def get_rawdata(self):
  997.         return self.rawdata
  998.  
  999.     def get_decdata(self):
  1000.         if self.decdata is not None:
  1001.             return self.decdata
  1002.         data = self.rawdata
  1003.         if self.decipher and data:
  1004.             # Handle encryption
  1005.             data = self.decipher(self.objid, self.genno, data)
  1006.         return data
  1007.  
  1008.        
  1009. ##  PDF Exceptions
  1010. ##
  1011. class PDFSyntaxError(PDFException): pass
  1012. class PDFNoValidXRef(PDFSyntaxError): pass
  1013. class PDFEncryptionError(PDFException): pass
  1014. class PDFPasswordIncorrect(PDFEncryptionError): pass
  1015.  
  1016. # some predefined literals and keywords.
  1017. LITERAL_OBJSTM = PSLiteralTable.intern('ObjStm')
  1018. LITERAL_XREF = PSLiteralTable.intern('XRef')
  1019. LITERAL_PAGE = PSLiteralTable.intern('Page')
  1020. LITERAL_PAGES = PSLiteralTable.intern('Pages')
  1021. LITERAL_CATALOG = PSLiteralTable.intern('Catalog')
  1022.  
  1023.  
  1024. ##  XRefs
  1025. ##
  1026.  
  1027. ##  PDFXRef
  1028. ##
  1029. class PDFXRef(object):
  1030.  
  1031.     def __init__(self):
  1032.         self.offsets = None
  1033.         return
  1034.  
  1035.     def __repr__(self):
  1036.         return '<PDFXRef: objs=%d>' % len(self.offsets)
  1037.  
  1038.     def objids(self):
  1039.         return self.offsets.iterkeys()
  1040.  
  1041.     def load(self, parser):
  1042.         self.offsets = {}
  1043.         while 1:
  1044.             try:
  1045.                 (pos, line) = parser.nextline()
  1046.             except PSEOF:
  1047.                 raise PDFNoValidXRef('Unexpected EOF - file corrupted?')
  1048.             if not line:
  1049.                 raise PDFNoValidXRef('Premature eof: %r' % parser)
  1050.             if line.startswith('trailer'):
  1051.                 parser.seek(pos)
  1052.                 break
  1053.             f = line.strip().split(' ')
  1054.             if len(f) != 2:
  1055.                 raise PDFNoValidXRef('Trailer not found: %r: line=%r' % (parser, line))
  1056.             try:
  1057.                 (start, nobjs) = map(int, f)
  1058.             except ValueError:
  1059.                 raise PDFNoValidXRef('Invalid line: %r: line=%r' % (parser, line))
  1060.             for objid in xrange(start, start+nobjs):
  1061.                 try:
  1062.                     (_, line) = parser.nextline()
  1063.                 except PSEOF:
  1064.                     raise PDFNoValidXRef('Unexpected EOF - file corrupted?')
  1065.                 f = line.strip().split(' ')
  1066.                 if len(f) != 3:
  1067.                     raise PDFNoValidXRef('Invalid XRef format: %r, line=%r' % (parser, line))
  1068.                 (pos, genno, use) = f
  1069.                 if use != 'n': continue
  1070.                 self.offsets[objid] = (int(genno), int(pos))
  1071.         self.load_trailer(parser)
  1072.         return
  1073.    
  1074.     KEYWORD_TRAILER = PSKeywordTable.intern('trailer')
  1075.     def load_trailer(self, parser):
  1076.         try:
  1077.             (_,kwd) = parser.nexttoken()
  1078.             assert kwd is self.KEYWORD_TRAILER
  1079.             (_,dic) = parser.nextobject(direct=True)
  1080.         except PSEOF:
  1081.             x = parser.pop(1)
  1082.             if not x:
  1083.                 raise PDFNoValidXRef('Unexpected EOF - file corrupted')
  1084.             (_,dic) = x[0]
  1085.         self.trailer = dict_value(dic)
  1086.         return
  1087.  
  1088.     def getpos(self, objid):
  1089.         try:
  1090.             (genno, pos) = self.offsets[objid]
  1091.         except KeyError:
  1092.             raise
  1093.         return (None, pos)
  1094.  
  1095.  
  1096. ##  PDFXRefStream
  1097. ##
  1098. class PDFXRefStream(object):
  1099.  
  1100.     def __init__(self):
  1101.         self.index = None
  1102.         self.data = None
  1103.         self.entlen = None
  1104.         self.fl1 = self.fl2 = self.fl3 = None
  1105.         return
  1106.  
  1107.     def __repr__(self):
  1108.         return '<PDFXRef: objids=%s>' % self.index
  1109.  
  1110.     def objids(self):
  1111.         for first, size in self.index:
  1112.             for objid in xrange(first, first + size):
  1113.                 yield objid
  1114.    
  1115.     def load(self, parser, debug=0):
  1116.         (_,objid) = parser.nexttoken() # ignored
  1117.         (_,genno) = parser.nexttoken() # ignored
  1118.         (_,kwd) = parser.nexttoken()
  1119.         (_,stream) = parser.nextobject()
  1120.         if not isinstance(stream, PDFStream) or \
  1121.            stream.dic['Type'] is not LITERAL_XREF:
  1122.             raise PDFNoValidXRef('Invalid PDF stream spec.')
  1123.         size = stream.dic['Size']
  1124.         index = stream.dic.get('Index', (0,size))
  1125.         self.index = zip(islice(index, 0, None, 2),
  1126.                          islice(index, 1, None, 2))
  1127.         (self.fl1, self.fl2, self.fl3) = stream.dic['W']
  1128.         self.data = stream.get_data()
  1129.         self.entlen = self.fl1+self.fl2+self.fl3
  1130.         self.trailer = stream.dic
  1131.         return
  1132.    
  1133.     def getpos(self, objid):
  1134.         offset = 0
  1135.         for first, size in self.index:
  1136.             if first <= objid  and objid < (first + size):
  1137.                 break
  1138.             offset += size
  1139.         else:
  1140.             raise KeyError(objid)
  1141.         i = self.entlen * ((objid - first) + offset)
  1142.         ent = self.data[i:i+self.entlen]
  1143.         f1 = nunpack(ent[:self.fl1], 1)
  1144.         if f1 == 1:
  1145.             pos = nunpack(ent[self.fl1:self.fl1+self.fl2])
  1146.             genno = nunpack(ent[self.fl1+self.fl2:])
  1147.             return (None, pos)
  1148.         elif f1 == 2:
  1149.             objid = nunpack(ent[self.fl1:self.fl1+self.fl2])
  1150.             index = nunpack(ent[self.fl1+self.fl2:])
  1151.             return (objid, index)
  1152.         # this is a free object
  1153.         raise KeyError(objid)
  1154.  
  1155.  
  1156. ##  PDFDocument
  1157. ##
  1158. ##  A PDFDocument object represents a PDF document.
  1159. ##  Since a PDF file is usually pretty big, normally it is not loaded
  1160. ##  at once. Rather it is parsed dynamically as processing goes.
  1161. ##  A PDF parser is associated with the document.
  1162. ##
  1163. class PDFDocument(object):
  1164.  
  1165.     def __init__(self):
  1166.         self.xrefs = []
  1167.         self.objs = {}
  1168.         self.parsed_objs = {}
  1169.         self.root = None
  1170.         self.catalog = None
  1171.         self.parser = None
  1172.         self.encryption = None
  1173.         self.decipher = None
  1174.         # dictionaries for fileopen
  1175.         self.fileopen = {}
  1176.         self.urlresult = {}        
  1177.         self.ready = False
  1178.         return
  1179.  
  1180.     # set_parser(parser)
  1181.     #   Associates the document with an (already initialized) parser object.
  1182.     def set_parser(self, parser):
  1183.         if self.parser: return
  1184.         self.parser = parser
  1185.         # The document is set to be temporarily ready during collecting
  1186.         # all the basic information about the document, e.g.
  1187.         # the header, the encryption information, and the access rights
  1188.         # for the document.
  1189.         self.ready = True
  1190.         # Retrieve the information of each header that was appended
  1191.         # (maybe multiple times) at the end of the document.
  1192.         self.xrefs = parser.read_xref()
  1193.         for xref in self.xrefs:
  1194.             trailer = xref.trailer
  1195.             if not trailer: continue
  1196.  
  1197.             # If there's an encryption info, remember it.
  1198.             if 'Encrypt' in trailer:
  1199.                 #assert not self.encryption
  1200.                 self.encryption = (list_value(trailer['ID']),
  1201.                                    dict_value(trailer['Encrypt']))
  1202.             if 'Root' in trailer:
  1203.                 self.set_root(dict_value(trailer['Root']))
  1204.                 break
  1205.         else:
  1206.             raise PDFSyntaxError('No /Root object! - Is this really a PDF?')
  1207.         # The document is set to be non-ready again, until all the
  1208.         # proper initialization (asking the password key and
  1209.         # verifying the access permission, so on) is finished.
  1210.         self.ready = False
  1211.         return
  1212.  
  1213.     # set_root(root)
  1214.     #   Set the Root dictionary of the document.
  1215.     #   Each PDF file must have exactly one /Root dictionary.
  1216.     def set_root(self, root):
  1217.         self.root = root
  1218.         self.catalog = dict_value(self.root)
  1219.         if self.catalog.get('Type') is not LITERAL_CATALOG:
  1220.             if STRICT:
  1221.                 raise PDFSyntaxError('Catalog not found!')
  1222.         return
  1223.     # initialize(password='')
  1224.     #   Perform the initialization with a given password.
  1225.     #   This step is mandatory even if there's no password associated
  1226.     #   with the document.
  1227.     def initialize(self, password=''):
  1228.         if not self.encryption:
  1229.             self.is_printable = self.is_modifiable = self.is_extractable = True
  1230.             self.ready = True
  1231.             return
  1232.         (docid, param) = self.encryption
  1233.         type = literal_name(param['Filter'])
  1234.         if type == 'Standard':
  1235.             return self.initialize_standard(password, docid, param)
  1236.         if type == 'EBX_HANDLER':
  1237.             return self.initialize_ebx(password, docid, param)
  1238.         if type == 'FOPN_foweb':
  1239.             return self.initialize_fopn(password, docid, param)        
  1240.         raise PDFEncryptionError('Unknown filter: param=%r' % param)
  1241.  
  1242.         # experimental fileopen support    
  1243.     def initialize_fopn(self, password, docid, param):
  1244.         self.is_printable = self.is_modifiable = self.is_extractable = True
  1245.         # get parameters and add it to the fo dictionary
  1246.         self.fileopen['Length'] = int_value(param.get('Length', 0)) / 8
  1247.         self.fileopen['VEID'] = str_value(param.get('VEID'))
  1248.         self.fileopen['BUILD'] = str_value(param.get('BUILD'))
  1249.         self.fileopen['SVID'] = str_value(param.get('SVID'))
  1250.         self.fileopen['DUID'] = str_value(param.get('DUID'))
  1251.         self.fileopen['V'] = int_value(param.get('V',2))        
  1252.         # crypt base
  1253.         rights = str_value(param.get('INFO')).decode('base64')
  1254.         rights = self.genkey_fileopeninfo(rights)
  1255.         #print rights        
  1256.         for pair in rights.split(';'):
  1257.             try:
  1258.                 key, value = pair.split('=')
  1259.                 self.fileopen[key] = value
  1260.             # fix for some misconfigured INFO variables
  1261.             except:
  1262.                 pass
  1263.         kattr = { 'SVID': 'ServiceID', 'DUID': 'DocumentID', 'I3ID': 'Ident3ID', \
  1264.                   'I4ID': 'Ident4ID', 'VERS': 'EncrVer', 'PRID': 'USR'}
  1265.         for keys in  kattr:
  1266.             try:
  1267.                 self.fileopen[kattr[keys]] = self.fileopen[keys]
  1268.                 del self.fileopen[keys]
  1269.             except:
  1270.                 continue
  1271.         # differentiate OS types
  1272.         sysplatform = sys.platform
  1273.         # if ostype is Windows
  1274.         if sysplatform=='win32':
  1275.             self.osuseragent = 'Windows NT 6.0'
  1276.             self.get_macaddress = self.get_win_macaddress
  1277.             self.fo_sethwids = self.fo_win_sethwids
  1278.         elif sysplatform=='linux2':
  1279.             adeptout = 'Linux is not supported, yet.\n'
  1280.             raise ADEPTError(adeptout)
  1281.             self.osuseragent = 'Linux i686'
  1282.             self.get_macaddress = self.get_linux_macaddress            
  1283.             self.fo_sethwids = self.fo_linux_sethwids            
  1284.         else:
  1285.             adeptout = ''
  1286.             adeptout = adeptout + 'Due to various privacy violations from Apple\n'
  1287.             adeptout = adeptout + 'Mac OS X support is disabled by default.'
  1288.             raise ADEPTError(adeptout)            
  1289.         # add static arguments for http/https request
  1290.         self.fo_setattributes()
  1291.         # add hardware specific arguments for http/https request        
  1292.         self.fo_sethwids()
  1293.         #print self.fileopen
  1294.         try:
  1295.             buildurl = self.fileopen['UURL']
  1296.         except:
  1297.             buildurl = self.fileopen['PURL']
  1298.         # fix for bad DPRM structure
  1299.         if self.fileopen['DPRM'][0] != r'/':
  1300.             self.fileopen['DPRM'] = r'/' + self.fileopen['DPRM']
  1301.         buildurl = buildurl + self.fileopen['DPRM'] + '?'
  1302.         buildurl = buildurl + 'Request=DocPerm'
  1303.         # great IdentID4 not present, keep on moving
  1304.         # is it a user/pw or a session authorization (1 sessionkey, 2 - user cookie)?
  1305.         if ('CSES' in self.fileopen) and (self.fileopen['CSES'] == 'sess')\
  1306.            and ('PFLG' in self.fileopen) and (self.fileopen['PFLG'] != '0'):
  1307.             self.pwtk = Tkinter.Tk()
  1308.             self.pwtk.title('Ineptpdf8')
  1309.             self.pwtk.minsize(150, 0)
  1310.             self.pwfieldreq = 0
  1311.             if 'SEMO' in self.fileopen:
  1312.                 # session cookie processing
  1313.                 if self.fileopen['SEMO'] == '1':
  1314.                     infotxt1 = 'Get the session cookie key manually (Firefox step-by-step:\n'+\
  1315.                                'Start Firefox -> Tools -> Options -> Privacy -> Show Cookies\n'+\
  1316.                                '-> Search for a cookie from ' + self.fileopen['CURL']+' with the\n'+\
  1317.                                'name sess and copy paste the content field in the Session Content field.)'
  1318.                     self.label0 = Tkinter.Label(self.pwtk, text=infotxt1)
  1319.                     self.label0.pack()
  1320.                     self.label1 = Tkinter.Label(self.pwtk, text="Session Content")                                    
  1321.                 # user cookie processing                                    
  1322.                 elif self.fileopen['SEMO'] == '2':
  1323.                     infotxt1 = 'Get the user cookie keys manually (Firefox step-by-step:\n'+\
  1324.                                'Start Firefox -> Tools -> Options -> Privacy -> Show Cookies\n'+\
  1325.                                '-> Search for cookies from ' + self.fileopen['CURL']+' with the\n'+\
  1326.                                'name name in the user field and copy paste the content field in the\n'+\
  1327.                                'username field. Do the same with the name pass in the password field).'
  1328.                     self.label0 = Tkinter.Label(self.pwtk, text=infotxt1)
  1329.                     self.label0.pack()                                      
  1330.                     self.label1 = Tkinter.Label(self.pwtk, text="Username")
  1331.                     self.pwfieldreq = 1
  1332.                 else:
  1333.                     self.label1 = Tkinter.Label(self.pwtk, text="Username")
  1334.                     self.pwfieldreq = 1                                      
  1335.             self.un_entry = Tkinter.Entry(self.pwtk)
  1336.             # cursor here
  1337.             self.un_entry.focus()
  1338.             self.label2 = Tkinter.Label(self.pwtk, text="Password")
  1339.             self.pw_entry = Tkinter.Entry(self.pwtk, show="*")
  1340.             self.button = Tkinter.Button(self.pwtk, text='Go for it!', command=self.fo_save_values)
  1341.             # widget layout, stack vertical
  1342.             self.label1.pack()
  1343.             self.un_entry.pack()
  1344.             # create a password label and field
  1345.             if self.pwfieldreq == 1:
  1346.                 self.label2.pack()
  1347.                 self.pw_entry.pack()
  1348.             self.button.pack()
  1349.             self.pwtk.update()            
  1350.             # start the event loop
  1351.             self.pwtk.mainloop()
  1352.  
  1353.         # drive through tupple for building the url
  1354.         burl = ( 'Stamp', 'Mode', 'USR', 'ServiceID', 'DocumentID',\
  1355.                  'Ident3ID', 'Ident4ID','DocStrFmt', 'OSType', 'Language',\
  1356.                  'LngLCID', 'LngRFC1766', 'LngISO4Char', 'Build', 'ProdVer', 'EncrVer',\
  1357.                  'Machine', 'Disk', 'Uuid', 'User', 'SaUser', 'SaSID',\
  1358.                  'FormHFT', 'UserName', 'UserPass', 'Session', \
  1359.                  'SelServer', 'AcroVersion', 'AcroProduct', 'AcroReader',\
  1360.                  'AcroCanEdit', 'AcroPrefIDib', 'InBrowser', 'CliAppName',\
  1361.                  'DocIsLocal', 'DocPathUrl', 'VolName', 'VolType', 'VolSN',\
  1362.                  'FSName', 'FowpKbd', 'OSBuild', 'RequestSchema')
  1363.         for keys in burl:
  1364.             try:
  1365.                 buildurl = buildurl + '&' + keys + '=' + self.fileopen[keys]
  1366.             except:
  1367.                 continue
  1368.         #print 'url:'
  1369.         #print buildurl
  1370.         # custom user agent identification?
  1371.         try:
  1372.             useragent = self.fileopen['AGEN']
  1373.             urllib.URLopener.version = useragent
  1374.         # attribute doesn't exist - take the default user agent
  1375.         except:
  1376.             urllib.URLopener.version = self.osuseragent
  1377.         # try to open the url
  1378.         try:
  1379.             u = urllib2.urlopen(buildurl)
  1380.             u.geturl()
  1381.             result = u.read()
  1382.         except:
  1383.             raise ADEPTError('No internet connection or a blocking firewall!')
  1384.         #print 'preresult'
  1385.         #print result
  1386.         if (result[0:8] == 'RetVal=1') or (result[0:8] == 'RetVal=2'):
  1387.             for pair in result.split('&'):
  1388.                 try:
  1389.                     key, value = pair.split('=')
  1390.                     self.urlresult[key] = value
  1391.                 except:
  1392.                     pass
  1393.         else:
  1394.             raise ADEPTError(result)    
  1395.         #print result
  1396.         try:
  1397.             if self.fileopen['Length'] == len(self.urlresult['Code']):
  1398.                 self.decrypt_key = self.urlresult['Code']
  1399.             else:
  1400.                 self.decrypt_key = self.urlresult['Code'].decode('hex')
  1401.         except:
  1402.             raise ADEPTError('Cannot find decryption key')
  1403.         self.genkey = self.genkey_fo
  1404.         self.decipher = self.decrypt_rc4
  1405.         self.ready = True
  1406.         return
  1407.  
  1408.     # user/password dialog    
  1409.     def fo_save_values(self):
  1410.         getout = 0
  1411.         username = 0
  1412.         password = 0
  1413.         username = self.un_entry.get()
  1414.         if self.pwfieldreq == 1:        
  1415.             password = self.pw_entry.get()
  1416.         un_length = len(username)
  1417.         if self.pwfieldreq == 1:                
  1418.             pw_length = len(password)
  1419.         if (un_length != 0):
  1420.             if self.pwfieldreq == 1:
  1421.                 if (pw_length != 0):
  1422.                     getout = 1
  1423.             else:
  1424.                 getout = 1
  1425.         if getout == 1:
  1426.             self.fileopen['UserName'] = urllib.quote(username)
  1427.             if self.pwfieldreq == 1:
  1428.                 self.fileopen['UserPass'] = urllib.quote(password)
  1429.             else:
  1430.                 self.fileopen['UserPass'] = self.fileopen['UserName']
  1431.             # doesn't always close the password window, who
  1432.             # knows why (Tkinter secrets ;=))
  1433.             self.pwtk.quit()
  1434.      
  1435.            
  1436.     def initialize_ebx(self, password, docid, param):
  1437.         global KEYFILEPATH
  1438.         self.is_printable = self.is_modifiable = self.is_extractable = True
  1439.         # keyfile path is wrong
  1440.         if len(KEYFILEPATH) > 5:
  1441.             errortext = KEYFILEPATH
  1442.             KEYFILEPATH = ''
  1443.             raise ADEPTError(errortext)
  1444.         with open(password, 'rb') as f:
  1445.             keyder = f.read()
  1446.         key = ASN1Parser([ord(x) for x in keyder])
  1447.         key = [bytesToNumber(key.getChild(x).value) for x in xrange(1, 4)]
  1448.         rsa = RSA.construct(key)
  1449.         length = int_value(param.get('Length', 0)) / 8
  1450.         rights = str_value(param.get('ADEPT_LICENSE')).decode('base64')
  1451.         rights = zlib.decompress(rights, -15)
  1452.         rights = etree.fromstring(rights)
  1453.         expr = './/{http://ns.adobe.com/adept}encryptedKey'
  1454.         bookkey = ''.join(rights.findtext(expr)).decode('base64')
  1455.         bookkey = rsa.decrypt(bookkey)
  1456.         if bookkey[0] != '\x02':
  1457.             raise ADEPTError('error decrypting book session key')
  1458.         index = bookkey.index('\0') + 1
  1459.         bookkey = bookkey[index:]
  1460.         ebx_V = int_value(param.get('V', 4))
  1461.         ebx_type = int_value(param.get('EBX_ENCRYPTIONTYPE', 6))
  1462.         # added because of the booktype / decryption book session key error
  1463.         if ebx_V == 3:
  1464.             V = 3        
  1465.         elif ebx_V < 4 or ebx_type < 6:
  1466.             V = ord(bookkey[0])
  1467.             bookkey = bookkey[1:]
  1468.         else:
  1469.             V = 2
  1470.         if length and len(bookkey) != length:
  1471.             raise ADEPTError('error decrypting book session key')
  1472.         self.decrypt_key = bookkey
  1473.         self.genkey = self.genkey_v3 if V == 3 else self.genkey_v2
  1474.         self.decipher = self.decrypt_rc4
  1475.         self.ready = True
  1476.         return
  1477.  
  1478.    
  1479.     PASSWORD_PADDING = '(\xbfN^Nu\x8aAd\x00NV\xff\xfa\x01\x08..' \
  1480.                        '\x00\xb6\xd0h>\x80/\x0c\xa9\xfedSiz'
  1481.     def initialize_standard(self, password, docid, param):
  1482.         V = int_value(param.get('V', 0))
  1483.         if not (V == 1 or V == 2):
  1484.             raise PDFEncryptionError('Unknown algorithm: param=%r' % param)
  1485.         length = int_value(param.get('Length', 40)) # Key length (bits)
  1486.         O = str_value(param['O'])
  1487.         R = int_value(param['R']) # Revision
  1488.         if 5 <= R:
  1489.             raise PDFEncryptionError('Unknown revision: %r' % R)
  1490.         U = str_value(param['U'])
  1491.         P = int_value(param['P'])
  1492.         self.is_printable = bool(P & 4)        
  1493.         self.is_modifiable = bool(P & 8)
  1494.         self.is_extractable = bool(P & 16)
  1495.         # Algorithm 3.2
  1496.         password = (password+self.PASSWORD_PADDING)[:32] # 1
  1497.         hash = hashlib.md5(password) # 2
  1498.         hash.update(O) # 3
  1499.         hash.update(struct.pack('<l', P)) # 4
  1500.         hash.update(docid[0]) # 5
  1501.         if 4 <= R:
  1502.             # 6
  1503.             raise PDFNotImplementedError(
  1504.                 'Revision 4 encryption is currently unsupported')
  1505.         if 3 <= R:
  1506.             # 8
  1507.             for _ in xrange(50):
  1508.                 hash = hashlib.md5(hash.digest()[:length/8])
  1509.         key = hash.digest()[:length/8]
  1510.         if R == 2:
  1511.             # Algorithm 3.4
  1512.             u1 = ARC4.new(key).decrypt(password)
  1513.         elif R == 3:
  1514.             # Algorithm 3.5
  1515.             hash = hashlib.md5(self.PASSWORD_PADDING) # 2
  1516.             hash.update(docid[0]) # 3
  1517.             x = ARC4.new(key).decrypt(hash.digest()[:16]) # 4
  1518.             for i in xrange(1,19+1):
  1519.                 k = ''.join( chr(ord(c) ^ i) for c in key )
  1520.                 x = ARC4.new(k).decrypt(x)
  1521.             u1 = x+x # 32bytes total
  1522.         if R == 2:
  1523.             is_authenticated = (u1 == U)
  1524.         else:
  1525.             is_authenticated = (u1[:16] == U[:16])
  1526.         if not is_authenticated:
  1527.             raise PDFPasswordIncorrect
  1528.         self.decrypt_key = key
  1529.         self.genkey = self.genkey_v2
  1530.         self.decipher = self.decipher_rc4  # XXX may be AES
  1531.         self.ready = True
  1532.         return
  1533.  
  1534.     def genkey_v2(self, objid, genno):
  1535.         objid = struct.pack('<L', objid)[:3]
  1536.         genno = struct.pack('<L', genno)[:2]
  1537.         key = self.decrypt_key + objid + genno
  1538.         hash = hashlib.md5(key)
  1539.         key = hash.digest()[:min(len(self.decrypt_key) + 5, 16)]
  1540.         return key
  1541.    
  1542.     def genkey_v3(self, objid, genno):
  1543.         objid = struct.pack('<L', objid ^ 0x3569ac)
  1544.         genno = struct.pack('<L', genno ^ 0xca96)
  1545.         key = self.decrypt_key
  1546.         key += objid[0] + genno[0] + objid[1] + genno[1] + objid[2] + 'sAlT'
  1547.         hash = hashlib.md5(key)
  1548.         key = hash.digest()[:min(len(self.decrypt_key) + 5, 16)]
  1549.         return key
  1550.  
  1551.     def genkey_fo(self, objid, genno):
  1552.         objid = struct.pack('<L', objid)[:3]
  1553.         genno = struct.pack('<L', genno)[:2]
  1554.         key =  self.decrypt_key + objid + genno
  1555.         hash = hashlib.md5(key)
  1556.         key = hash.digest()[:min(len(self.decrypt_key) + 5, 16)]
  1557.         return key
  1558.    
  1559.     def decrypt_rc4(self, objid, genno, data):
  1560.         key = self.genkey(objid, genno)
  1561.         return ARC4.new(key).decrypt(data)
  1562.    
  1563.     def fo_setattributes(self):
  1564.         self.fileopen['Request']='DocPerm'
  1565.         self.fileopen['Mode']='CNR'
  1566.         self.fileopen['DocStrFmt']='ASCII'
  1567.         self.fileopen['Language']='ENU'
  1568.         self.fileopen['LngLCID']='ENU'
  1569.         self.fileopen['LngRFC1766']='en'
  1570.         self.fileopen['LngISO4Char']='en-us'
  1571.         self.fileopen['ProdVer']='1.8.7.8'
  1572.         self.fileopen['FormHFT']='Yes'
  1573.         self.fileopen['SelServer']='Yes'
  1574.         self.fileopen['AcroCanEdit']='Yes'
  1575.         self.fileopen['AcroPrefIDib']='Yes'
  1576.         self.fileopen['InBrowser']='Unk'
  1577.         self.fileopen['CliAppName']=''
  1578.         self.fileopen['DocIsLocal']='Yes'
  1579.         self.fileopen['FowpKbd']='No'
  1580.         self.fileopen['RequestSchema']='Default'
  1581.         self.fileopen['VolType']='Fixed'
  1582.        
  1583.     # get nic mac address
  1584.     def get_linux_macaddress(self):
  1585.         try:
  1586.             for line in os.popen("/sbin/ifconfig"):
  1587.                 if line.find('Ether') > -1:
  1588.                     mac = line.split()[4]
  1589.                     break
  1590.             #print mac.replace(':','')
  1591.             return mac.replace(':','')
  1592.         except:
  1593.             raise ADEPTError('Cannot find MAC address. Get forum help.')
  1594.  
  1595.     def get_win_macaddress(self):
  1596.         try:
  1597.             gasize = c_ulong(5000)
  1598.             p = create_string_buffer(5000)
  1599.             GetAdaptersInfo = windll.iphlpapi.GetAdaptersInfo
  1600.             print GetAdaptersInfo(byref(p),byref(gasize))
  1601.             return p[0x194:0x19a].encode('hex')
  1602.         except:
  1603.             raise ADEPTError('Cannot find MAC address. Get forum help.')
  1604.        
  1605.     # custom conversion 5 bytes to 8 chars method
  1606.     def fo_convert5to8(self, edisk):
  1607.         # byte to number/char mapping table
  1608.         darray=[0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,\
  1609.                 0x46,0x47,0x48,0x4A,0x4B,0x4C,0x4D,0x4E,0x50,0x51,0x52,0x53,0x54,\
  1610.                 0x55,0x56,0x57,0x58,0x59,0x5A]
  1611.         pdid = struct.pack('<I', int(edisk[0:4].encode("hex"),16))
  1612.         pdid = int(pdid.encode("hex"),16)
  1613.         outputhw = ''
  1614.         # disk id processing
  1615.         for i in range(0,6):
  1616.             index = pdid & 0x1f
  1617.             # shift the disk id 5 bits to the right
  1618.             pdid = pdid >> 5
  1619.             outputhw = outputhw + chr(darray[index])
  1620.         pdid = (ord(edisk[4]) << 2)|pdid
  1621.         # get the last 2 bits from the hwid + low part of the cpuid
  1622.         for i in range(0,2):
  1623.             index = pdid & 0x1f
  1624.             # shift the disk id 5 bits to the right
  1625.             pdid = pdid >> 5
  1626.             outputhw = outputhw + chr(darray[index])
  1627.         return outputhw
  1628.  
  1629.     # Linux processing
  1630.     def fo_linux_sethwids(self):
  1631.         # linux specific attributes
  1632.         self.fileopen['OSType']='Linux'
  1633.         self.fileopen['AcroProduct']='AcroReader'
  1634.         self.fileopen['AcroReader']='Yes'
  1635.         self.fileopen['AcroVersion']='9.101'
  1636.         self.fileopen['FSName']='ext3'    
  1637.         self.fileopen['Build']='878'
  1638.         self.fileopen['ProdVer']='1.8.5.1'
  1639.         self.fileopen['OSBuild']='2.6.33'        
  1640.         # write hardware keys
  1641.         hwkey = 0
  1642.         pmac = self.get_macaddress().decode("hex");
  1643.         self.fileopen['Disk'] = self.fo_convert5to8(pmac[1:])
  1644.         # get primary used default mac address
  1645.         self.fileopen['Machine'] = self.fo_convert5to8(pmac[1:])
  1646.         # get uuid
  1647.         # check for reversed offline handler
  1648.         if 'Ident4ID' in self.fileopen:
  1649.             self.fileopen['User'] = getpass.getuser()
  1650.             self.fileopen['SaUser'] = getpass.getuser()
  1651.             try:
  1652.                 cuser = winreg.HKEY_CURRENT_USER
  1653.                 FOW3_UUID = 'Software\\Fileopen'
  1654.                 regkey = winreg.OpenKey(cuser, FOW3_UUID)
  1655.                 userkey = winreg.QueryValueEx(regkey, 'Fowp3Uuid')[0]
  1656.                 #print userkey
  1657.                 if self.genkey_cryptmach(userkey)[0:4] == 'ec20':
  1658.                     self.fileopen['Uuid'] = self.genkey_cryptmach(userkey)[4:]
  1659.                 else:
  1660.                     raise ADEPTError('Unsupported Ident4D Decryption,\n'+\
  1661.                                      'Report the bug to the ineptpdf script forum')
  1662.             except:
  1663.                 raise ADEPTError('Cannot find FowP3Uuid file')
  1664.         else:
  1665.             self.fileopen['Uuid'] = str(uuid.uuid1())
  1666.         # get time stamp
  1667.         self.fileopen['Stamp'] = str(time.time())[:-3]
  1668.         # get fileopen input pdf name + path
  1669.         self.fileopen['DocPathUrl'] = 'file%3a%2f%2f%2f' + INPUTFILEPATH
  1670. ##        # get volume name (urllib quote necessairy?) urllib.quote(
  1671. ##        self.fileopen['VolName'] = win32api.GetVolumeInformation("C:\\")[0]
  1672. ##        # get volume serial number
  1673. ##        self.fileopen['VolSN'] = str(win32api.GetVolumeInformation("C:\\")[1])
  1674.         return
  1675.  
  1676.     # Windows processing
  1677.     def fo_win_sethwids(self):
  1678.         # Windows specific attributes        
  1679.         self.fileopen['OSType']='Windows'
  1680.         self.fileopen['AcroProduct']='Exchange-Pro'
  1681.         self.fileopen['AcroReader']='No'    
  1682.         self.fileopen['FSName']='NTFS'
  1683.         self.fileopen['OSBuild']='7600'
  1684.         self.fileopen['AcroReader']='No'
  1685.         self.fileopen['AcroVersion']='9.256'
  1686.         self.fileopen['Build']='878'        
  1687.         # write hardware keys
  1688.         hwkey = 0
  1689.         # get the os type and save it in ostype
  1690.         try:
  1691.             import win32api
  1692.             import _winreg as winreg                
  1693.         except:
  1694.             raise ADEPTError('Python 2.6 + included win32api module needed.')
  1695.         v0 = win32api.GetVolumeInformation('C:\\')
  1696.         v1 = win32api.GetSystemInfo()[6]
  1697.         volserial = v0[1]
  1698.         lowcpu = v1 & 255
  1699.         highcpu = (v1 >> 8) & 255
  1700.         volserial = struct.pack('<L', int(volserial))
  1701.         lowcpu   = struct.pack('B', lowcpu)
  1702.         highcpu = struct.pack('B',highcpu)
  1703.         # save it to the fo dictionary
  1704.         encrypteddisk = volserial + lowcpu + highcpu
  1705.         self.fileopen['Disk'] = self.fo_convert5to8(encrypteddisk)
  1706.         # get primary used default mac address
  1707.         pmac = self.get_macaddress().decode("hex");
  1708.         self.fileopen['Machine'] = self.fo_convert5to8(pmac[1:])
  1709.         # get uuid
  1710.         # check for reversed offline handler
  1711.         if 'Ident4ID' in self.fileopen:
  1712.             self.fileopen['User'] = getpass.getuser()
  1713.             self.fileopen['SaUser'] = getpass.getuser()
  1714.             try:
  1715.                 cuser = winreg.HKEY_CURRENT_USER
  1716.                 FOW3_UUID = 'Software\\Fileopen'
  1717.                 regkey = winreg.OpenKey(cuser, FOW3_UUID)
  1718.                 userkey = winreg.QueryValueEx(regkey, 'Fowp3Uuid')[0]
  1719.  
  1720.             except:
  1721.                 try:
  1722.                     fopath = os.environ['AppData']+'\\FileOpen\\'
  1723.                     fofilename = 'Fowpmadi.txt'
  1724.                     f = open(fopath+fofilename, 'rb')
  1725.                     userkey = f.read()
  1726.                     userkey = userkey[0:40]
  1727.                 except:
  1728.                     raise ADEPTError('Cannot find FowP3Uuid in registry or file')
  1729.                 #print userkey
  1730.             if self.genkey_cryptmach(userkey)[0:4] == 'ec20':
  1731.                 self.fileopen['Uuid'] = self.genkey_cryptmach(userkey)[4:]
  1732.             else:
  1733.                 raise ADEPTError('Unsupported Ident4D Decryption,\n'+\
  1734.                                  'Report the bug to the ineptpdf script forum')                
  1735.         else:
  1736.             self.fileopen['Uuid'] = str(uuid.uuid1())
  1737.         # get time stamp
  1738.         self.fileopen['Stamp'] = str(time.time())[:-3]
  1739.         # get fileopen input pdf name + path
  1740.         self.fileopen['DocPathUrl'] = 'file%3a%2f%2f%2f' + INPUTFILEPATH
  1741.         # get volume name (urllib quote necessairy?) urllib.quote(
  1742.         self.fileopen['VolName'] = win32api.GetVolumeInformation("C:\\")[0]
  1743.         # get volume serial number
  1744.         self.fileopen['VolSN'] = str(win32api.GetVolumeInformation("C:\\")[1])
  1745.         return
  1746.  
  1747.     # decryption routine for the INFO area
  1748.     def genkey_fileopeninfo(self, data):
  1749.         input1 = struct.pack('L', 0xa4da49de)
  1750.         seed   = struct.pack('B', 0x82)
  1751.         key = input1[3] + input1[2] +input1[1] +input1[0] + seed
  1752.         hash = hashlib.md5()
  1753.         key = hash.update(key)
  1754.         spointer4 = struct.pack('<L', 0xec8d6c58)
  1755.         seed = struct.pack('B', 0x07)
  1756.         key = spointer4[3] + spointer4[2] + spointer4[1] + spointer4[0] + seed
  1757.         key = hash.update(key)
  1758.         md5 = hash.digest()
  1759.         key = md5[0:10]
  1760.         return ARC4.new(key).decrypt(data)
  1761.  
  1762.     def genkey_cryptmach(self, data):
  1763.         # decryption byte stream
  1764.         key_string = '37A4DA49DE82064939A60B1D8D7B5F0F8873B6D93E'.decode('hex')
  1765.         username = self.fileopen['User']
  1766.         m = hashlib.md5()
  1767.         m.update(key_string[:3])
  1768.         m.update(username[:13]) # max 13 characters 13 - sizeof(username)
  1769.         if (13 - len(username)) > 0:
  1770.             m.update(key_string[:(13-len(username))])
  1771.         key = m.digest()[0:16]
  1772.         return ARC4.new(key).decrypt(data)
  1773.    
  1774.     KEYWORD_OBJ = PSKeywordTable.intern('obj')
  1775.    
  1776.     def getobj(self, objid):
  1777.         if not self.ready:
  1778.             raise PDFException('PDFDocument not initialized')
  1779.         #assert self.xrefs
  1780.         if objid in self.objs:
  1781.             genno = 0
  1782.             obj = self.objs[objid]
  1783.         else:
  1784.             for xref in self.xrefs:
  1785.                 try:
  1786.                     (stmid, index) = xref.getpos(objid)
  1787.                     break
  1788.                 except KeyError:
  1789.                     pass
  1790.             else:
  1791.                 #if STRICT:
  1792.                 #    raise PDFSyntaxError('Cannot locate objid=%r' % objid)
  1793.                 return None
  1794.             if stmid:
  1795.                 if gen_xref_stm:
  1796.                     return PDFObjStmRef(objid, stmid, index)
  1797. # Stuff from pdfminer: extract objects from object stream
  1798.                 stream = stream_value(self.getobj(stmid))
  1799.                 if stream.dic.get('Type') is not LITERAL_OBJSTM:
  1800.                     if STRICT:
  1801.                         raise PDFSyntaxError('Not a stream object: %r' % stream)
  1802.                 try:
  1803.                     n = stream.dic['N']
  1804.                 except KeyError:
  1805.                     if STRICT:
  1806.                         raise PDFSyntaxError('N is not defined: %r' % stream)
  1807.                     n = 0
  1808.  
  1809.                 if stmid in self.parsed_objs:
  1810.                     objs = self.parsed_objs[stmid]
  1811.                 else:
  1812.                     parser = PDFObjStrmParser(stream.get_data(), self)
  1813.                     objs = []
  1814.                     try:
  1815.                         while 1:
  1816.                             (_,obj) = parser.nextobject()
  1817.                             objs.append(obj)
  1818.                     except PSEOF:
  1819.                         pass
  1820.                     self.parsed_objs[stmid] = objs
  1821.                 genno = 0
  1822.                 i = n*2+index
  1823.                 try:
  1824.                     obj = objs[i]
  1825.                 except IndexError:
  1826.                     raise PDFSyntaxError('Invalid object number: objid=%r' % (objid))
  1827.                 if isinstance(obj, PDFStream):
  1828.                     obj.set_objid(objid, 0)
  1829. ###
  1830.             else:
  1831.                 self.parser.seek(index)
  1832.                 (_,objid1) = self.parser.nexttoken() # objid
  1833.                 (_,genno) = self.parser.nexttoken() # genno
  1834.                 #assert objid1 == objid, (objid, objid1)
  1835.                 (_,kwd) = self.parser.nexttoken()
  1836.                 if kwd is not self.KEYWORD_OBJ:
  1837.                     raise PDFSyntaxError(
  1838.                         'Invalid object spec: offset=%r' % index)
  1839.                 (_,obj) = self.parser.nextobject()
  1840.                 if isinstance(obj, PDFStream):
  1841.                     obj.set_objid(objid, genno)
  1842.                 if self.decipher:
  1843.                     obj = decipher_all(self.decipher, objid, genno, obj)
  1844.             self.objs[objid] = obj
  1845.         return obj
  1846.  
  1847. class PDFObjStmRef(object):
  1848.     maxindex = 0
  1849.     def __init__(self, objid, stmid, index):
  1850.         self.objid = objid
  1851.         self.stmid = stmid
  1852.         self.index = index
  1853.         if index > PDFObjStmRef.maxindex:
  1854.             PDFObjStmRef.maxindex = index
  1855.  
  1856.    
  1857. ##  PDFParser
  1858. ##
  1859. class PDFParser(PSStackParser):
  1860.  
  1861.     def __init__(self, doc, fp):
  1862.         PSStackParser.__init__(self, fp)
  1863.         self.doc = doc
  1864.         self.doc.set_parser(self)
  1865.         return
  1866.  
  1867.     def __repr__(self):
  1868.         return '<PDFParser>'
  1869.  
  1870.     KEYWORD_R = PSKeywordTable.intern('R')
  1871.     KEYWORD_ENDOBJ = PSKeywordTable.intern('endobj')
  1872.     KEYWORD_STREAM = PSKeywordTable.intern('stream')
  1873.     KEYWORD_XREF = PSKeywordTable.intern('xref')
  1874.     KEYWORD_STARTXREF = PSKeywordTable.intern('startxref')
  1875.     def do_keyword(self, pos, token):
  1876.         if token in (self.KEYWORD_XREF, self.KEYWORD_STARTXREF):
  1877.             self.add_results(*self.pop(1))
  1878.             return
  1879.         if token is self.KEYWORD_ENDOBJ:
  1880.             self.add_results(*self.pop(4))
  1881.             return
  1882.        
  1883.         if token is self.KEYWORD_R:
  1884.             # reference to indirect object
  1885.             try:
  1886.                 ((_,objid), (_,genno)) = self.pop(2)
  1887.                 (objid, genno) = (int(objid), int(genno))
  1888.                 obj = PDFObjRef(self.doc, objid, genno)
  1889.                 self.push((pos, obj))
  1890.             except PSSyntaxError:
  1891.                 pass
  1892.             return
  1893.            
  1894.         if token is self.KEYWORD_STREAM:
  1895.             # stream object
  1896.             ((_,dic),) = self.pop(1)
  1897.             dic = dict_value(dic)
  1898.             try:
  1899.                 objlen = int_value(dic['Length'])
  1900.             except KeyError:
  1901.                 if STRICT:
  1902.                     raise PDFSyntaxError('/Length is undefined: %r' % dic)
  1903.                 objlen = 0
  1904.             self.seek(pos)
  1905.             try:
  1906.                 (_, line) = self.nextline()  # 'stream'
  1907.             except PSEOF:
  1908.                 if STRICT:
  1909.                     raise PDFSyntaxError('Unexpected EOF')
  1910.                 return
  1911.             pos += len(line)
  1912.             self.fp.seek(pos)
  1913.             data = self.fp.read(objlen)
  1914.             self.seek(pos+objlen)
  1915.             while 1:
  1916.                 try:
  1917.                     (linepos, line) = self.nextline()
  1918.                 except PSEOF:
  1919.                     if STRICT:
  1920.                         raise PDFSyntaxError('Unexpected EOF')
  1921.                     break
  1922.                 if 'endstream' in line:
  1923.                     i = line.index('endstream')
  1924.                     objlen += i
  1925.                     data += line[:i]
  1926.                     break
  1927.                 objlen += len(line)
  1928.                 data += line
  1929.             self.seek(pos+objlen)
  1930.             obj = PDFStream(dic, data, self.doc.decipher)
  1931.             self.push((pos, obj))
  1932.             return
  1933.        
  1934.         # others
  1935.         self.push((pos, token))
  1936.         return
  1937.  
  1938.     def find_xref(self):
  1939.         # search the last xref table by scanning the file backwards.
  1940.         prev = None
  1941.         for line in self.revreadlines():
  1942.             line = line.strip()
  1943.             if line == 'startxref': break
  1944.             if line:
  1945.                 prev = line
  1946.         else:
  1947.             raise PDFNoValidXRef('Unexpected EOF')
  1948.         return int(prev)
  1949.  
  1950.     # read xref table
  1951.     def read_xref_from(self, start, xrefs):
  1952.         self.seek(start)
  1953.         self.reset()
  1954.         try:
  1955.             (pos, token) = self.nexttoken()
  1956.         except PSEOF:
  1957.             raise PDFNoValidXRef('Unexpected EOF')
  1958.         if isinstance(token, int):
  1959.             # XRefStream: PDF-1.5
  1960.             if GEN_XREF_STM == 1:
  1961.                 global gen_xref_stm
  1962.                 gen_xref_stm = True
  1963.             self.seek(pos)
  1964.             self.reset()
  1965.             xref = PDFXRefStream()
  1966.             xref.load(self)
  1967.         else:
  1968.             if token is not self.KEYWORD_XREF:
  1969.                 raise PDFNoValidXRef('xref not found: pos=%d, token=%r' %
  1970.                                      (pos, token))
  1971.             self.nextline()
  1972.             xref = PDFXRef()
  1973.             xref.load(self)
  1974.         xrefs.append(xref)
  1975.         trailer = xref.trailer
  1976.         if 'XRefStm' in trailer:
  1977.             pos = int_value(trailer['XRefStm'])
  1978.             self.read_xref_from(pos, xrefs)
  1979.         if 'Prev' in trailer:
  1980.             # find previous xref
  1981.             pos = int_value(trailer['Prev'])
  1982.             self.read_xref_from(pos, xrefs)
  1983.         return
  1984.        
  1985.     # read xref tables and trailers
  1986.     def read_xref(self):
  1987.         xrefs = []
  1988.         trailerpos = None
  1989.         try:
  1990.             pos = self.find_xref()
  1991.             self.read_xref_from(pos, xrefs)
  1992.         except PDFNoValidXRef:
  1993.             # fallback
  1994.             self.seek(0)
  1995.             pat = re.compile(r'^(\d+)\s+(\d+)\s+obj\b')
  1996.             offsets = {}
  1997.             xref = PDFXRef()
  1998.             while 1:
  1999.                 try:
  2000.                     (pos, line) = self.nextline()
  2001.                 except PSEOF:
  2002.                     break
  2003.                 if line.startswith('trailer'):
  2004.                     trailerpos = pos # remember last trailer
  2005.                 m = pat.match(line)
  2006.                 if not m: continue
  2007.                 (objid, genno) = m.groups()
  2008.                 offsets[int(objid)] = (0, pos)
  2009.             if not offsets: raise
  2010.             xref.offsets = offsets
  2011.             if trailerpos:
  2012.                 self.seek(trailerpos)
  2013.                 xref.load_trailer(self)
  2014.                 xrefs.append(xref)
  2015.         return xrefs
  2016.  
  2017. ##  PDFObjStrmParser
  2018. ##
  2019. class PDFObjStrmParser(PDFParser):
  2020.  
  2021.     def __init__(self, data, doc):
  2022.         PSStackParser.__init__(self, StringIO(data))
  2023.         self.doc = doc
  2024.         return
  2025.  
  2026.     def flush(self):
  2027.         self.add_results(*self.popall())
  2028.         return
  2029.  
  2030.     KEYWORD_R = KWD('R')
  2031.     def do_keyword(self, pos, token):
  2032.         if token is self.KEYWORD_R:
  2033.             # reference to indirect object
  2034.             try:
  2035.                 ((_,objid), (_,genno)) = self.pop(2)
  2036.                 (objid, genno) = (int(objid), int(genno))
  2037.                 obj = PDFObjRef(self.doc, objid, genno)
  2038.                 self.push((pos, obj))
  2039.             except PSSyntaxError:
  2040.                 pass
  2041.             return
  2042.         # others
  2043.         self.push((pos, token))
  2044.         return
  2045.  
  2046. ###
  2047. ### My own code, for which there is none else to blame
  2048.  
  2049. class PDFSerializer(object):
  2050.     def __init__(self, inf, keypath):
  2051.         global GEN_XREF_STM, gen_xref_stm
  2052.         gen_xref_stm = GEN_XREF_STM > 1
  2053.         self.version = inf.read(8)
  2054.         inf.seek(0)
  2055.         self.doc = doc = PDFDocument()
  2056.         parser = PDFParser(doc, inf)
  2057.         doc.initialize(keypath)
  2058.         self.objids = objids = set()
  2059.         for xref in reversed(doc.xrefs):
  2060.             trailer = xref.trailer
  2061.             for objid in xref.objids():
  2062.                 objids.add(objid)
  2063.         trailer = dict(trailer)
  2064.         trailer.pop('Prev', None)
  2065.         trailer.pop('XRefStm', None)
  2066.         if 'Encrypt' in trailer:
  2067.             objids.remove(trailer.pop('Encrypt').objid)
  2068.         self.trailer = trailer
  2069.  
  2070.     def dump(self, outf):
  2071.         self.outf = outf
  2072.         self.write(self.version)
  2073.         self.write('\n%\xe2\xe3\xcf\xd3\n')
  2074.         doc = self.doc
  2075.         objids = self.objids
  2076.         xrefs = {}
  2077.         maxobj = max(objids)
  2078.         trailer = dict(self.trailer)
  2079.         trailer['Size'] = maxobj + 1
  2080.         for objid in objids:
  2081.             obj = doc.getobj(objid)
  2082.             if isinstance(obj, PDFObjStmRef):
  2083.                 xrefs[objid] = obj
  2084.                 continue
  2085.             if obj is not None:
  2086.                 try:
  2087.                     genno = obj.genno
  2088.                 except AttributeError:
  2089.                     genno = 0
  2090.                 xrefs[objid] = (self.tell(), genno)
  2091.                 self.serialize_indirect(objid, obj)
  2092.         startxref = self.tell()
  2093.  
  2094.         if not gen_xref_stm:
  2095.             self.write('xref\n')
  2096.             self.write('0 %d\n' % (maxobj + 1,))
  2097.             for objid in xrange(0, maxobj + 1):
  2098.                 if objid in xrefs:
  2099.                     # force the genno to be 0
  2100.                     self.write("%010d 00000 n \n" % xrefs[objid][0])
  2101.                 else:
  2102.                     self.write("%010d %05d f \n" % (0, 65535))
  2103.            
  2104.             self.write('trailer\n')
  2105.             self.serialize_object(trailer)
  2106.             self.write('\nstartxref\n%d\n%%%%EOF' % startxref)
  2107.  
  2108.         else: # Generate crossref stream.
  2109.  
  2110.             # Calculate size of entries
  2111.             maxoffset = max(startxref, maxobj)
  2112.             maxindex = PDFObjStmRef.maxindex
  2113.             fl2 = 2
  2114.             power = 65536
  2115.             while maxoffset >= power:
  2116.                 fl2 += 1
  2117.                 power *= 256
  2118.             fl3 = 1
  2119.             power = 256
  2120.             while maxindex >= power:
  2121.                 fl3 += 1
  2122.                 power *= 256
  2123.                    
  2124.             index = []
  2125.             first = None
  2126.             prev = None
  2127.             data = []
  2128.             # Put the xrefstream's reference in itself
  2129.             startxref = self.tell()
  2130.             maxobj += 1
  2131.             xrefs[maxobj] = (startxref, 0)
  2132.             for objid in sorted(xrefs):
  2133.                 if first is None:
  2134.                     first = objid
  2135.                 elif objid != prev + 1:
  2136.                     index.extend((first, prev - first + 1))
  2137.                     first = objid
  2138.                 prev = objid
  2139.                 objref = xrefs[objid]
  2140.                 if isinstance(objref, PDFObjStmRef):
  2141.                     f1 = 2
  2142.                     f2 = objref.stmid
  2143.                     f3 = objref.index
  2144.                 else:
  2145.                     f1 = 1
  2146.                     f2 = objref[0]
  2147.                     # we force all generation numbers to be 0
  2148.                     # f3 = objref[1]
  2149.                     f3 = 0
  2150.                
  2151.                 data.append(struct.pack('>B', f1))
  2152.                 data.append(struct.pack('>L', f2)[-fl2:])
  2153.                 data.append(struct.pack('>L', f3)[-fl3:])
  2154.             index.extend((first, prev - first + 1))
  2155.             data = zlib.compress(''.join(data))
  2156.             dic = {'Type': LITERAL_XREF, 'Size': prev + 1, 'Index': index,
  2157.                    'W': [1, fl2, fl3], 'Length': len(data),
  2158.                    'Filter': LITERALS_FLATE_DECODE[0],
  2159.                    'Root': trailer['Root'],}
  2160.             if 'Info' in trailer:
  2161.                 dic['Info'] = trailer['Info']
  2162.             xrefstm = PDFStream(dic, data)
  2163.             self.serialize_indirect(maxobj, xrefstm)
  2164.             self.write('startxref\n%d\n%%%%EOF' % startxref)
  2165.  
  2166.     def write(self, data):
  2167.         self.outf.write(data)
  2168.         self.last = data[-1:]
  2169.  
  2170.     def tell(self):
  2171.         return self.outf.tell()
  2172.  
  2173.     def escape_string(self, string):
  2174.         string = string.replace('\\', '\\\\')
  2175.         string = string.replace('\n', r'\n')
  2176.         string = string.replace('(', r'\(')
  2177.         string = string.replace(')', r'\)')
  2178.          # get rid of ciando id
  2179.         regularexp = re.compile(r'http://www.ciando.com/index.cfm/intRefererID/\d{5}')
  2180.         if regularexp.match(string): return ('http://www.ciando.com')
  2181.         return string
  2182.    
  2183.     def serialize_object(self, obj):
  2184.         if isinstance(obj, dict):
  2185.             # Correct malformed Mac OS resource forks for Stanza
  2186.             if 'ResFork' in obj and 'Type' in obj and 'Subtype' not in obj \
  2187.                    and isinstance(obj['Type'], int):
  2188.                 obj['Subtype'] = obj['Type']
  2189.                 del obj['Type']
  2190.             # end - hope this doesn't have bad effects
  2191.             self.write('<<')
  2192.             for key, val in obj.items():
  2193.                 self.write('/%s' % key)
  2194.                 self.serialize_object(val)
  2195.             self.write('>>')
  2196.         elif isinstance(obj, list):
  2197.             self.write('[')
  2198.             for val in obj:
  2199.                 self.serialize_object(val)
  2200.             self.write(']')
  2201.         elif isinstance(obj, str):
  2202.             self.write('(%s)' % self.escape_string(obj))
  2203.         elif isinstance(obj, bool):
  2204.             if self.last.isalnum():
  2205.                 self.write(' ')
  2206.             self.write(str(obj).lower())            
  2207.         elif isinstance(obj, (int, long, float)):
  2208.             if self.last.isalnum():
  2209.                 self.write(' ')
  2210.             self.write(str(obj))
  2211.         elif isinstance(obj, PDFObjRef):
  2212.             if self.last.isalnum():
  2213.                 self.write(' ')            
  2214.             self.write('%d %d R' % (obj.objid, 0))
  2215.         elif isinstance(obj, PDFStream):
  2216.             ### If we don't generate cross ref streams the object streams
  2217.             ### are no longer useful, as we have extracted all objects from
  2218.             ### them. Therefore leave them out from the output.
  2219.             if obj.dic.get('Type') == LITERAL_OBJSTM and not gen_xref_stm:
  2220.                     self.write('(deleted)')
  2221.             else:
  2222.                 data = obj.get_decdata()
  2223.                 self.serialize_object(obj.dic)
  2224.                 self.write('stream\n')
  2225.                 self.write(data)
  2226.                 self.write('\nendstream')
  2227.         else:
  2228.             data = str(obj)
  2229.             if data[0].isalnum() and self.last.isalnum():
  2230.                 self.write(' ')
  2231.             self.write(data)
  2232.    
  2233.     def serialize_indirect(self, objid, obj):
  2234.         self.write('%d 0 obj' % (objid,))
  2235.         self.serialize_object(obj)
  2236.         if self.last.isalnum():
  2237.             self.write('\n')
  2238.         self.write('endobj\n')
  2239.  
  2240. def cli_main(argv=sys.argv):
  2241.     progname = os.path.basename(argv[0])
  2242.     if RSA is None:
  2243.         print "%s: This script requires PyCrypto, which must be installed " \
  2244.               "separately.  Read the top-of-script comment for details." % \
  2245.               (progname,)
  2246.         return 1
  2247.     if len(argv) != 4:
  2248.         print "usage: %s KEYFILE INBOOK OUTBOOK" % (progname,)
  2249.         return 1
  2250.     keypath, inpath, outpath = argv[1:]
  2251.     with open(inpath, 'rb') as inf:
  2252.         serializer = PDFSerializer(inf, keypath)
  2253.         with open(outpath, 'wb') as outf:
  2254.             serializer.dump(outf)
  2255.     return 0
  2256.  
  2257.  
  2258. class DecryptionDialog(Tkinter.Frame):
  2259.     def __init__(self, root):
  2260.         Tkinter.Frame.__init__(self, root, border=5)
  2261.         ltext='Select file for decryption\n(Ignore Key file option for Fileopen PDFs)'        
  2262.         self.status = Tkinter.Label(self, text=ltext)
  2263.         self.status.pack(fill=Tkconstants.X, expand=1)
  2264.         body = Tkinter.Frame(self)
  2265.         body.pack(fill=Tkconstants.X, expand=1)
  2266.         sticky = Tkconstants.E + Tkconstants.W
  2267.         body.grid_columnconfigure(1, weight=2)
  2268.         Tkinter.Label(body, text='Key file').grid(row=0)
  2269.         self.keypath = Tkinter.Entry(body, width=30)
  2270.         self.keypath.grid(row=0, column=1, sticky=sticky)
  2271.         if os.path.exists('adeptkey.der'):
  2272.             self.keypath.insert(0, 'adeptkey.der')
  2273.         button = Tkinter.Button(body, text="...", command=self.get_keypath)
  2274.         button.grid(row=0, column=2)
  2275.         Tkinter.Label(body, text='Input file').grid(row=1)
  2276.         self.inpath = Tkinter.Entry(body, width=30)
  2277.         self.inpath.grid(row=1, column=1, sticky=sticky)
  2278.         button = Tkinter.Button(body, text="...", command=self.get_inpath)
  2279.         button.grid(row=1, column=2)
  2280.         Tkinter.Label(body, text='Output file').grid(row=2)
  2281.         self.outpath = Tkinter.Entry(body, width=30)
  2282.         self.outpath.grid(row=2, column=1, sticky=sticky)
  2283.         button = Tkinter.Button(body, text="...", command=self.get_outpath)
  2284.         button.grid(row=2, column=2)
  2285.         buttons = Tkinter.Frame(self)
  2286.         buttons.pack()
  2287.         botton = Tkinter.Button(
  2288.             buttons, text="Decrypt", width=10, command=self.decrypt)
  2289.         botton.pack(side=Tkconstants.LEFT)
  2290.         Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
  2291.         button = Tkinter.Button(
  2292.             buttons, text="Quit", width=10, command=self.quit)
  2293.         button.pack(side=Tkconstants.RIGHT)
  2294.  
  2295.     def get_keypath(self):
  2296.         keypath = tkFileDialog.askopenfilename(
  2297.             parent=None, title='Select ADEPT key file',
  2298.             defaultextension='.der', filetypes=[('DER-encoded files', '.der'),
  2299.                                                 ('All Files', '.*')])
  2300.         if keypath:
  2301.             keypath = os.path.normpath(keypath)
  2302.             self.keypath.delete(0, Tkconstants.END)
  2303.             self.keypath.insert(0, keypath)
  2304.         return
  2305.  
  2306.     def get_inpath(self):
  2307.         inpath = tkFileDialog.askopenfilename(
  2308.             parent=None, title='Select ADEPT-encrypted PDF file to decrypt',
  2309.             defaultextension='.pdf', filetypes=[('PDF files', '.pdf'),
  2310.                                                  ('All files', '.*')])
  2311.         if inpath:
  2312.             inpath = os.path.normpath(inpath)
  2313.             self.inpath.delete(0, Tkconstants.END)
  2314.             self.inpath.insert(0, inpath)
  2315.         return
  2316.  
  2317.     def get_outpath(self):
  2318.         outpath = tkFileDialog.asksaveasfilename(
  2319.             parent=None, title='Select unencrypted PDF file to produce',
  2320.             defaultextension='.pdf', filetypes=[('PDF files', '.pdf'),
  2321.                                                  ('All files', '.*')])
  2322.         if outpath:
  2323.             outpath = os.path.normpath(outpath)
  2324.             self.outpath.delete(0, Tkconstants.END)
  2325.             self.outpath.insert(0, outpath)
  2326.         return
  2327.  
  2328.     def decrypt(self):
  2329.         global INPUTFILEPATH
  2330.         global KEYFILEPATH
  2331.         keypath = self.keypath.get()
  2332.         inpath = self.inpath.get()
  2333.         outpath = self.outpath.get()
  2334.         if not keypath or not os.path.exists(keypath):
  2335.             # keyfile doesn't exist
  2336.             KEYFILEPATH = 'Specific keyfile (adeptkey.der) does not exist or path is wrong!'
  2337.         if not inpath or not os.path.exists(inpath):
  2338.             self.status['text'] = 'Specified input file does not exist'
  2339.             return
  2340.         if not outpath:
  2341.             self.status['text'] = 'Output file not specified'
  2342.             return
  2343.         if inpath == outpath:
  2344.             self.status['text'] = 'Must have different input and output files'
  2345.             return
  2346.         INPUTFILEPATH = urllib.quote(inpath)        
  2347.         argv = [sys.argv[0], keypath, inpath, outpath]
  2348.         self.status['text'] = 'Processing ...'
  2349.         try:
  2350.             cli_main(argv)
  2351.         except Exception, e:
  2352.             self.status['text'] = 'Error: ' + str(e)
  2353.             return
  2354.         self.status['text'] = 'File successfully decrypted'
  2355.  
  2356. def gui_main():
  2357.     root = Tkinter.Tk()
  2358.     if RSA is None:
  2359.         root.withdraw()
  2360.         tkMessageBox.showerror(
  2361.             "INEPT PDF and FileOpen Decrypter",
  2362.             "This script requires PyCrypto, which must be installed "
  2363.             "separately.  Read the top-of-script comment for details.")
  2364.         return 1
  2365.     root.title('INEPT PDF Decrypter 8.4.2 (FileOpen Support)')
  2366.     root.resizable(True, False)
  2367.     root.minsize(320, 0)
  2368.     DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
  2369.     root.mainloop()
  2370.     return 0
  2371.  
  2372.  
  2373. if __name__ == '__main__':
  2374.     if len(sys.argv) > 1:
  2375.         sys.exit(cli_main())
  2376.     sys.exit(gui_main())
Add Comment
Please, Sign In to add comment