origami 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. data/COPYING.LESSER +165 -0
  2. data/README +77 -0
  3. data/VERSION +1 -0
  4. data/bin/config/pdfcop.conf.yml +237 -0
  5. data/bin/gui/about.rb +46 -0
  6. data/bin/gui/config.rb +132 -0
  7. data/bin/gui/file.rb +385 -0
  8. data/bin/gui/hexdump.rb +74 -0
  9. data/bin/gui/hexview.rb +91 -0
  10. data/bin/gui/imgview.rb +72 -0
  11. data/bin/gui/menu.rb +392 -0
  12. data/bin/gui/properties.rb +132 -0
  13. data/bin/gui/signing.rb +635 -0
  14. data/bin/gui/textview.rb +107 -0
  15. data/bin/gui/treeview.rb +409 -0
  16. data/bin/gui/walker.rb +282 -0
  17. data/bin/gui/xrefs.rb +79 -0
  18. data/bin/pdf2graph +121 -0
  19. data/bin/pdf2ruby +353 -0
  20. data/bin/pdfcocoon +104 -0
  21. data/bin/pdfcop +455 -0
  22. data/bin/pdfdecompress +104 -0
  23. data/bin/pdfdecrypt +95 -0
  24. data/bin/pdfencrypt +112 -0
  25. data/bin/pdfextract +221 -0
  26. data/bin/pdfmetadata +123 -0
  27. data/bin/pdfsh +13 -0
  28. data/bin/pdfwalker +7 -0
  29. data/bin/shell/.irbrc +104 -0
  30. data/bin/shell/console.rb +136 -0
  31. data/bin/shell/hexdump.rb +83 -0
  32. data/origami.rb +36 -0
  33. data/origami/3d.rb +239 -0
  34. data/origami/acroform.rb +321 -0
  35. data/origami/actions.rb +299 -0
  36. data/origami/adobe/fdf.rb +259 -0
  37. data/origami/adobe/ppklite.rb +489 -0
  38. data/origami/annotations.rb +775 -0
  39. data/origami/array.rb +187 -0
  40. data/origami/boolean.rb +101 -0
  41. data/origami/catalog.rb +486 -0
  42. data/origami/destinations.rb +213 -0
  43. data/origami/dictionary.rb +188 -0
  44. data/origami/docmdp.rb +96 -0
  45. data/origami/encryption.rb +1293 -0
  46. data/origami/export.rb +283 -0
  47. data/origami/file.rb +222 -0
  48. data/origami/filters.rb +250 -0
  49. data/origami/filters/ascii.rb +189 -0
  50. data/origami/filters/ccitt.rb +515 -0
  51. data/origami/filters/crypt.rb +47 -0
  52. data/origami/filters/dct.rb +61 -0
  53. data/origami/filters/flate.rb +112 -0
  54. data/origami/filters/jbig2.rb +63 -0
  55. data/origami/filters/jpx.rb +53 -0
  56. data/origami/filters/lzw.rb +195 -0
  57. data/origami/filters/predictors.rb +276 -0
  58. data/origami/filters/runlength.rb +117 -0
  59. data/origami/font.rb +209 -0
  60. data/origami/functions.rb +93 -0
  61. data/origami/graphics.rb +33 -0
  62. data/origami/graphics/colors.rb +191 -0
  63. data/origami/graphics/instruction.rb +126 -0
  64. data/origami/graphics/path.rb +154 -0
  65. data/origami/graphics/patterns.rb +180 -0
  66. data/origami/graphics/state.rb +164 -0
  67. data/origami/graphics/text.rb +224 -0
  68. data/origami/graphics/xobject.rb +493 -0
  69. data/origami/header.rb +90 -0
  70. data/origami/linearization.rb +318 -0
  71. data/origami/metadata.rb +114 -0
  72. data/origami/name.rb +170 -0
  73. data/origami/null.rb +75 -0
  74. data/origami/numeric.rb +188 -0
  75. data/origami/obfuscation.rb +233 -0
  76. data/origami/object.rb +527 -0
  77. data/origami/outline.rb +59 -0
  78. data/origami/page.rb +559 -0
  79. data/origami/parser.rb +268 -0
  80. data/origami/parsers/fdf.rb +45 -0
  81. data/origami/parsers/pdf.rb +27 -0
  82. data/origami/parsers/pdf/linear.rb +113 -0
  83. data/origami/parsers/ppklite.rb +86 -0
  84. data/origami/pdf.rb +1144 -0
  85. data/origami/reference.rb +113 -0
  86. data/origami/signature.rb +474 -0
  87. data/origami/stream.rb +575 -0
  88. data/origami/string.rb +416 -0
  89. data/origami/trailer.rb +173 -0
  90. data/origami/webcapture.rb +87 -0
  91. data/origami/xfa.rb +3027 -0
  92. data/origami/xreftable.rb +447 -0
  93. data/templates/patterns.rb +66 -0
  94. data/templates/widgets.rb +173 -0
  95. data/templates/xdp.rb +92 -0
  96. data/tests/dataset/test.dummycrt +28 -0
  97. data/tests/dataset/test.dummykey +27 -0
  98. data/tests/tc_actions.rb +32 -0
  99. data/tests/tc_annotations.rb +85 -0
  100. data/tests/tc_pages.rb +37 -0
  101. data/tests/tc_pdfattach.rb +24 -0
  102. data/tests/tc_pdfencrypt.rb +110 -0
  103. data/tests/tc_pdfnew.rb +32 -0
  104. data/tests/tc_pdfparse.rb +98 -0
  105. data/tests/tc_pdfsig.rb +37 -0
  106. data/tests/tc_streams.rb +129 -0
  107. data/tests/ts_pdf.rb +45 -0
  108. metadata +193 -0
@@ -0,0 +1,268 @@
1
+ =begin
2
+
3
+ = File
4
+ parser.rb
5
+
6
+ = Info
7
+ Origami is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU Lesser General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ Origami is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU Lesser General Public License for more details.
16
+
17
+ You should have received a copy of the GNU Lesser General Public License
18
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ =end
21
+
22
+ require 'strscan'
23
+
24
+ module Origami
25
+
26
+ if RUBY_PLATFORM =~ /win32/ or RUBY_PLATFORM =~ /mingw32/
27
+ require "Win32API"
28
+
29
+ getStdHandle = Win32API.new("kernel32", "GetStdHandle", ['L'], 'L')
30
+ @@setConsoleTextAttribute = Win32API.new("kernel32", "SetConsoleTextAttribute", ['L', 'N'], 'I')
31
+
32
+ @@hOut = getStdHandle.call(-11)
33
+ end
34
+
35
+ module Colors #:nodoc;
36
+ if RUBY_PLATFORM =~ /win32/ or RUBY_PLATFORM =~ /mingw32/
37
+ BLACK = 0
38
+ BLUE = 1
39
+ GREEN = 2
40
+ CYAN = 3
41
+ RED = 4
42
+ MAGENTA = 5
43
+ YELLOW = 6
44
+ GREY = 7
45
+ WHITE = 8
46
+ else
47
+ GREY = '0;0'
48
+ BLACK = '0;30'
49
+ RED = '0;31'
50
+ GREEN = '0;32'
51
+ YELLOW = '0;33'
52
+ BLUE = '0;34'
53
+ MAGENTA = '0;35'
54
+ CYAN = '0;36'
55
+ WHITE = '0;37'
56
+ BRIGHT_GREY = '1;30'
57
+ BRIGHT_RED = '1;31'
58
+ BRIGHT_GREEN = '1;32'
59
+ BRIGHT_YELLOW = '1;33'
60
+ BRIGHT_BLUE = '1;34'
61
+ BRIGHT_MAGENTA = '1;35'
62
+ BRIGHT_CYAN = '1;36'
63
+ BRIGHT_WHITE = '1;37'
64
+ end
65
+ end
66
+
67
+ def set_fg_color(color, bright = false, fd = STDOUT) #:nodoc:
68
+ if RUBY_PLATFORM =~ /win32/ or RUBY_PLATFORM =~ /mingw32/
69
+ if bright then color |= Colors::WHITE end
70
+ @@setConsoleTextAttribute.call(@@hOut, color)
71
+ yield
72
+ @@setConsoleTextAttribute.call(@@hOut, Colors::GREY)
73
+ else
74
+ col, nocol = [color, Colors::GREY].map! { |key| "\033[#{key}m" }
75
+ fd << col
76
+ yield
77
+ fd << nocol
78
+ end
79
+ end
80
+
81
+ unless RUBY_PLATFORM =~ /win32/ or RUBY_PLATFORM =~ /mingw32/
82
+ def colorize(text, color, bright = false)
83
+ col, nocol = [color, Colors::GREY].map! { |key| "\033[#{key}m" }
84
+ "#{col}#{text}#{nocol}"
85
+ end
86
+ end
87
+
88
+ def colorprint(text, color, bright = false, fd = STDOUT) #:nodoc:
89
+ set_fg_color(color, bright, fd) {
90
+ fd << text
91
+ }
92
+ end
93
+
94
+ EOL = "\r\n" #:nodoc:
95
+ DEFINED_TOKENS = "[<\\[(%\\/)\\]>]" #:nodoc:
96
+ WHITESPACES = "([ \\f\\t\\r\\n\\0]|%[^\\n]*\\n)*" #:nodoc:
97
+ WHITECHARS = "[ \\f\\t\\r\\n\\0]*" #:nodoc:
98
+ WHITECHARS_NORET = "[ \\f\\t\\0]*" #:nodoc:
99
+
100
+ REGEXP_WHITESPACES = Regexp.new(WHITESPACES) #:nodoc:
101
+
102
+ class Parser #:nodoc:
103
+
104
+ class ParsingError < Exception #:nodoc:
105
+ end
106
+
107
+ #
108
+ # Do not output debug information.
109
+ #
110
+ VERBOSE_QUIET = 0
111
+
112
+ #
113
+ # Output some useful information.
114
+ #
115
+ VERBOSE_INFO = 1
116
+
117
+ #
118
+ # Output debug information.
119
+ #
120
+ VERBOSE_DEBUG = 2
121
+
122
+ #
123
+ # Output every objects read
124
+ #
125
+ VERBOSE_INSANE = 3
126
+
127
+ attr_accessor :options
128
+
129
+ def initialize(options = {}) #:nodoc:
130
+
131
+ #Default options values
132
+ @options =
133
+ {
134
+ :verbosity => VERBOSE_INFO, # Verbose level.
135
+ :ignore_errors => true, # Try to keep on parsing when errors occur.
136
+ :callback => Proc.new {}, # Callback procedure whenever a structure is read.
137
+ :password => '', # Default password being tried when opening a protected document.
138
+ :prompt_password => Proc.new { print "Password: "; gets.chomp }, # Callback procedure to prompt password when document is encrypted.
139
+ :force => false # Force PDF header detection
140
+ }
141
+
142
+ @options.update(options)
143
+ end
144
+
145
+ def parse(stream)
146
+ data =
147
+ if stream.respond_to? :read
148
+ if ''.respond_to? :force_encoding
149
+ StringScanner.new(stream.read.force_encoding('binary')) # 1.9 compat
150
+ else
151
+ StringScanner.new(stream.read)
152
+ end
153
+ elsif stream.is_a? ::String
154
+ if ''.respond_to? :force_encoding
155
+ StringScanner.new(File.open(stream, "r", :encoding => 'binary').binmode.read)
156
+ else
157
+ StringScanner.new(File.open(stream, "r").binmode.read)
158
+ end
159
+ elsif stream.is_a? StringScanner
160
+ stream
161
+ else
162
+ raise TypeError
163
+ end
164
+
165
+ @data = data
166
+ @data.pos = 0
167
+ end
168
+
169
+ def parse_objects(file) #:nodoc:
170
+ begin
171
+ loop do
172
+ obj = Object.parse(@data)
173
+ return if obj.nil?
174
+
175
+ trace "Read #{obj.type} object#{if obj.type != obj.real_type then " (" + obj.real_type.to_s.split('::').last + ")" end}, #{obj.reference}"
176
+
177
+ file << obj
178
+
179
+ @options[:callback].call(obj)
180
+ end
181
+
182
+ rescue UnterminatedObjectError => e
183
+ error e.message
184
+ file << e.obj
185
+
186
+ @options[:callback].call(e.obj)
187
+
188
+ Object.skip_until_next_obj(@data)
189
+ retry
190
+
191
+ rescue Exception => e
192
+ error "Breaking on: #{(@data.peek(10) + "...").inspect} at offset 0x#{@data.pos.to_s(16)}"
193
+ error "Last exception: [#{e.class}] #{e.message}"
194
+ debug "-> Stopped reading body : #{file.revisions.last.body.size} indirect objects have been parsed" if file.is_a?(PDF)
195
+ abort("Manually fix the file or set :ignore_errors parameter.") if not @options[:ignore_errors]
196
+
197
+ debug 'Skipping this indirect object.'
198
+ raise(e) if not Object.skip_until_next_obj(@data)
199
+
200
+ retry
201
+ end
202
+ end
203
+
204
+ def parse_xreftable(file) #:nodoc:
205
+ begin
206
+ info "...Parsing xref table..."
207
+ file.revisions.last.xreftable = XRef::Section.parse(@data)
208
+ @options[:callback].call(file.revisions.last.xreftable)
209
+ rescue Exception => e
210
+ debug "Exception caught while parsing xref table : " + e.message
211
+ warn "Unable to parse xref table! Xrefs might be stored into an XRef stream."
212
+
213
+ @data.pos -= 'trailer'.length unless @data.skip_until(/trailer/).nil?
214
+ end
215
+ end
216
+
217
+ def parse_trailer(file) #:nodoc:
218
+ begin
219
+ info "...Parsing trailer..."
220
+ trailer = Trailer.parse(@data)
221
+
222
+ if file.is_a?(PDF)
223
+ xrefstm = file.get_object_by_offset(trailer.startxref) ||
224
+ (file.get_object_by_offset(trailer.XRefStm) if trailer.has_field? :XRefStm)
225
+ end
226
+
227
+ if not xrefstm.nil?
228
+ debug "Found a XRefStream for this revision at #{xrefstm.reference}"
229
+ file.revisions.last.xrefstm = xrefstm
230
+ end
231
+
232
+ file.revisions.last.trailer = trailer
233
+ @options[:callback].call(file.revisions.last.trailer)
234
+
235
+ rescue Exception => e
236
+ debug "Exception caught while parsing trailer : " + e.message
237
+ warn "Unable to parse trailer!"
238
+
239
+ abort("Manually fix the file or set :ignore_errors parameter.") if not @options[:ignore_errors]
240
+
241
+ raise
242
+ end
243
+ end
244
+
245
+ private
246
+
247
+ def error(str = "") #:nodoc:
248
+ colorprint("[error] #{str}\n", Colors::RED, false, STDERR)
249
+ end
250
+
251
+ def warn(str = "") #:nodoc:
252
+ colorprint("[info ] Warning: #{str}\n", Colors::YELLOW, false, STDERR) if @options[:verbosity] >= VERBOSE_INFO
253
+ end
254
+
255
+ def info(str = "") #:nodoc:
256
+ (colorprint("[info ] ", Colors::GREEN, false, STDERR); STDERR << "#{str}\n") if @options[:verbosity] >= VERBOSE_INFO
257
+ end
258
+
259
+ def debug(str = "") #:nodoc:
260
+ (colorprint("[debug] ", Colors::MAGENTA, false, STDERR); STDERR << "#{str}\n") if @options[:verbosity] >= VERBOSE_DEBUG
261
+ end
262
+
263
+ def trace(str = "") #:nodoc:
264
+ (colorprint("[trace] ", Colors::CYAN, false, STDERR); STDERR << "#{str}\n") if @options[:verbosity] >= VERBOSE_INSANE
265
+ end
266
+ end
267
+ end
268
+
@@ -0,0 +1,45 @@
1
+ =begin
2
+
3
+ = File
4
+ parsers/fdf.rb
5
+
6
+ = Info
7
+ Origami is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU Lesser General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ Origami is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU Lesser General Public License for more details.
16
+
17
+ You should have received a copy of the GNU Lesser General Public License
18
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ =end
21
+
22
+ require 'origami/parser'
23
+ require 'origami/adobe/fdf'
24
+
25
+ module Origami
26
+
27
+ class Adobe::FDF
28
+ class Parser < Origami::Parser
29
+ def parse(stream) #:nodoc:
30
+ super
31
+
32
+ fdf = Adobe::FDF.new
33
+ fdf.header = Adobe::FDF::Header.parse(stream)
34
+ @options[:callback].call(fdf.header)
35
+
36
+ parse_objects(fdf)
37
+ parse_xreftable(fdf)
38
+ parse_trailer(fdf)
39
+
40
+ addrbk
41
+ end
42
+ end
43
+ end
44
+ end
45
+
@@ -0,0 +1,27 @@
1
+ =begin
2
+
3
+ = File
4
+ parsers/pdf.rb
5
+
6
+ = Info
7
+ This file is part of Origami, PDF manipulation framework for Ruby
8
+ Copyright (C) 2010 Guillaume Delugré <guillaume@security-labs.org>
9
+ All right reserved.
10
+
11
+ Origami is free software: you can redistribute it and/or modify
12
+ it under the terms of the GNU Lesser General Public License as published by
13
+ the Free Software Foundation, either version 3 of the License, or
14
+ (at your option) any later version.
15
+
16
+ Origami is distributed in the hope that it will be useful,
17
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ GNU Lesser General Public License for more details.
20
+
21
+ You should have received a copy of the GNU Lesser General Public License
22
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
23
+
24
+ =end
25
+
26
+ require 'origami/parsers/pdf/linear'
27
+
@@ -0,0 +1,113 @@
1
+ =begin
2
+
3
+ = File
4
+ parsers/linear.rb
5
+
6
+ = Info
7
+ Origami is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU Lesser General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ Origami is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU Lesser General Public License for more details.
16
+
17
+ You should have received a copy of the GNU Lesser General Public License
18
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ =end
21
+
22
+
23
+ require 'origami/parser'
24
+ require 'origami/pdf'
25
+
26
+ module Origami
27
+
28
+ class PDF
29
+
30
+ #
31
+ # Create a new PDF linear Parser.
32
+ #
33
+ class LinearParser < Origami::Parser
34
+ def parse(stream)
35
+ super
36
+
37
+ if @options[:force] == true
38
+ @data.skip_until(/%PDF-/).nil?
39
+ @data.pos = @data.pos - 5
40
+ end
41
+
42
+ pdf = PDF.new(false)
43
+
44
+ info "...Reading header..."
45
+ begin
46
+ pdf.header = PDF::Header.parse(@data)
47
+ @options[:callback].call(pdf.header)
48
+ rescue InvalidHeaderError => e
49
+ if @options[:ignore_errors] == true
50
+ warn "PDF header is invalid, ignoring..."
51
+ else
52
+ raise e
53
+ end
54
+ end
55
+
56
+ #
57
+ # Parse each revision
58
+ #
59
+ revision = 0
60
+ until @data.eos? do
61
+
62
+ begin
63
+
64
+ pdf.add_new_revision unless revision.zero?
65
+ revision = revision.succ
66
+
67
+ info "...Parsing revision #{pdf.revisions.size}..."
68
+ parse_objects(pdf)
69
+ parse_xreftable(pdf)
70
+ parse_trailer(pdf)
71
+
72
+ rescue SystemExit
73
+ raise
74
+ rescue Exception => e
75
+ error "Cannot read : " + (@data.peek(10) + "...").inspect
76
+ error "Stopped on exception : " + e.message
77
+
78
+ break
79
+ end
80
+
81
+ end
82
+
83
+ warn "This file has been linearized." if pdf.is_linearized?
84
+
85
+ #
86
+ # Decrypt encrypted file contents
87
+ #
88
+ if pdf.is_encrypted?
89
+ warn "This document contains encrypted data!"
90
+
91
+ passwd = @options[:password]
92
+ begin
93
+ pdf.decrypt(passwd)
94
+ rescue EncryptionInvalidPasswordError
95
+ if passwd.empty?
96
+ passwd = @options[:prompt_password].call
97
+ retry unless passwd.empty?
98
+ end
99
+
100
+ raise EncryptionInvalidPasswordError
101
+ end
102
+ end
103
+
104
+ if pdf.is_signed?
105
+ warn "This document has been signed!"
106
+ end
107
+
108
+ pdf
109
+ end
110
+ end
111
+ end
112
+ end
113
+