origami 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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,132 @@
1
+ =begin
2
+
3
+ = File
4
+ properties.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 'iconv'
27
+ require 'digest/md5'
28
+
29
+ module PDFWalker
30
+
31
+ class Walker < Window
32
+
33
+ def display_file_properties
34
+ if @opened
35
+ prop = Properties.new(self, @opened)
36
+ end
37
+ end
38
+
39
+ class Properties < Dialog
40
+
41
+ @@acrobat_versions =
42
+ {
43
+ 1.0 => "1.x",
44
+ 1.1 => "2.x",
45
+ 1.2 => "3.x",
46
+ 1.3 => "4.x",
47
+ 1.4 => "5.x",
48
+ 1.5 => "6.x",
49
+ 1.6 => "7.x",
50
+ 1.7 => "8.x / 9.x / 10.x"
51
+ }
52
+
53
+ def initialize(parent, pdf)
54
+ super("Document properties", parent, Dialog::MODAL, [Stock::CLOSE, Dialog::RESPONSE_NONE])
55
+
56
+ docframe = Frame.new(" File properties ")
57
+
58
+ i = Iconv.new("UTF-8//IGNORE//TRANSLIT", "ISO-8859-1")
59
+
60
+ stat = File.stat(parent.filename)
61
+ labels =
62
+ [
63
+ [ "Filename:", parent.filename ],
64
+ [ "File size:", "#{File.size(parent.filename)} bytes" ],
65
+ [ "MD5:", Digest::MD5.hexdigest(File.open(parent.filename).read) ],
66
+ [ "Read-only:", "#{not stat.writable?}" ],
67
+ [ "Creation date:", i.iconv("#{stat.ctime}") ],
68
+ [ "Last modified:", i.iconv("#{stat.mtime}") ]
69
+ ]
70
+ i.close
71
+
72
+ doctable = Table.new(labels.size + 1, 3)
73
+
74
+ row = 0
75
+ labels.each do |name, value|
76
+
77
+ doctable.attach(Label.new(name).set_alignment(1,0), 0, 1, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
78
+ doctable.attach(Label.new(value).set_alignment(0,0), 1, 2, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
79
+
80
+ row = row.succ
81
+ end
82
+
83
+ docframe.border_width = 5
84
+ docframe.shadow_type = Gtk::SHADOW_IN
85
+ docframe.add(doctable)
86
+
87
+ pdfframe = Frame.new(" PDF properties ")
88
+
89
+ labels =
90
+ [
91
+ [ "Version:", "#{pdf.header.to_f} (Acrobat #{ if pdf.header.to_f >= 1.0 and pdf.header.to_f <= 1.7 then @@acrobat_versions[pdf.header.to_f] else "unknown version" end})" ],
92
+ [ "Number of revisions:", "#{pdf.revisions.size}" ],
93
+ [ "Number of indirect objects:", "#{pdf.indirect_objects.size}" ],
94
+ [ "Number of pages:", "#{pdf.pages.size}" ],
95
+ [ "Is linearized:", "#{pdf.is_linearized?}" ],
96
+ [ "Is encrypted:", "#{pdf.is_encrypted?}" ],
97
+ [ "Is signed:", "#{pdf.is_signed?}" ],
98
+ [ "Has usage rights:", "#{pdf.has_usage_rights?}"],
99
+ [ "Contains Acroform:", "#{pdf.has_form?}" ],
100
+ #[ "Contains XFA forms:", "#{pdf.has_xfa_forms?}" ]
101
+ [ "Has document information:", "#{pdf.has_document_info?}" ],
102
+ [ "Has metadata:", "#{pdf.has_metadata?}" ]
103
+ ]
104
+
105
+ pdftable = Table.new(labels.size + 1, 3)
106
+
107
+ row = 0
108
+ labels.each do |name, value|
109
+
110
+ pdftable.attach(Label.new(name).set_alignment(1,0), 0, 1, row, row + 1, Gtk::FILL, Gtk::SHRINK, 4, 4)
111
+ pdftable.attach(Label.new(value).set_alignment(0,0), 1, 2, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
112
+
113
+ row = row.succ
114
+ end
115
+
116
+ pdfframe.border_width = 5
117
+ pdfframe.shadow_type = Gtk::SHADOW_IN
118
+ pdfframe.add(pdftable)
119
+
120
+ vbox.add(docframe)
121
+ vbox.add(pdfframe)
122
+
123
+ signal_connect('response') { destroy }
124
+
125
+ show_all
126
+ end
127
+
128
+ end
129
+
130
+ end
131
+
132
+ end
@@ -0,0 +1,635 @@
1
+ =begin
2
+
3
+ = File
4
+ signing.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
+ module PDFWalker
27
+
28
+ class Walker < Window
29
+
30
+ def display_signing_wizard
31
+
32
+ if @opened
33
+ SignWizard.new(self, @opened)
34
+ end
35
+
36
+ end
37
+
38
+ def display_usage_rights_wizard
39
+
40
+ if @opened
41
+ UsageRightsWizard.new(self, @opened)
42
+ end
43
+
44
+ end
45
+
46
+ class UsageRightsWizard < Assistant
47
+
48
+ def initialize(parent, pdf)
49
+
50
+ super()
51
+
52
+ @parent = parent
53
+
54
+ @pkey, @cert = nil, nil
55
+
56
+ create_intro_page
57
+ create_rights_selection_page
58
+ create_termination_page
59
+
60
+ signal_connect('delete_event') { self.destroy }
61
+ signal_connect('cancel') { self.destroy }
62
+ signal_connect('close') { self.destroy }
63
+
64
+ signal_connect('apply') {
65
+
66
+ rights = []
67
+
68
+ rights << UsageRights::Rights::DOCUMENT_FULLSAVE if @document_fullsave.active?
69
+
70
+ rights << UsageRights::Rights::ANNOTS_CREATE if @annots_create.active?
71
+ rights << UsageRights::Rights::ANNOTS_DELETE if @annots_delete.active?
72
+ rights << UsageRights::Rights::ANNOTS_MODIFY if @annots_modify.active?
73
+ rights << UsageRights::Rights::ANNOTS_COPY if @annots_copy.active?
74
+ rights << UsageRights::Rights::ANNOTS_IMPORT if @annots_import.active?
75
+ rights << UsageRights::Rights::ANNOTS_EXPORT if @annots_export.active?
76
+ rights << UsageRights::Rights::ANNOTS_ONLINE if @annots_online.active?
77
+ rights << UsageRights::Rights::ANNOTS_SUMMARYVIEW if @annots_sumview.active?
78
+
79
+ rights << UsageRights::Rights::FORM_FILLIN if @form_fillin.active?
80
+ rights << UsageRights::Rights::FORM_IMPORT if @form_import.active?
81
+ rights << UsageRights::Rights::FORM_EXPORT if @form_export.active?
82
+ rights << UsageRights::Rights::FORM_SUBMITSTANDALONE if @form_submit.active?
83
+ rights << UsageRights::Rights::FORM_SPAWNTEMPLATE if @form_spawntemplate.active?
84
+ rights << UsageRights::Rights::FORM_BARCODEPLAINTEXT if @form_barcode.active?
85
+ rights << UsageRights::Rights::FORM_ONLINE if @form_online.active?
86
+
87
+ rights << UsageRights::Rights::SIGNATURE_MODIFY if @signature_modify.active?
88
+
89
+ rights << UsageRights::Rights::EF_CREATE if @ef_create.active?
90
+ rights << UsageRights::Rights::EF_DELETE if @ef_delete.active?
91
+ rights << UsageRights::Rights::EF_MODIFY if @ef_modify.active?
92
+ rights << UsageRights::Rights::EF_IMPORT if @ef_import.active?
93
+
94
+ begin
95
+ pdf.enable_usage_rights(*rights)
96
+
97
+ set_page_title(@lastpage, "Usage Rights have been enabled")
98
+ @msg_status.text = "Usage Rights have been enabled for the current document.\n You should consider saving it now."
99
+
100
+ @parent.reload
101
+ rescue Exception => e
102
+ puts e
103
+ puts e.backtrace
104
+
105
+ set_page_title(@lastpage, "Usage Rights have not been enabled")
106
+ @msg_status.text = "An error occured during the signature process."
107
+ end
108
+ }
109
+
110
+ show_all
111
+
112
+ end
113
+
114
+ private
115
+
116
+ def create_intro_page
117
+
118
+ intro = <<INTRO
119
+ You are about to enable Usage Rights for the current PDF document.
120
+ To enable these features, you need to have an Adobe public/private key pair in your possession.
121
+
122
+ Make sure you have adobe.crt and adobe.key located in the current directory.
123
+ INTRO
124
+
125
+ vbox = VBox.new(false, 5)
126
+ vbox.set_border_width(5)
127
+
128
+ lbl = Label.new(intro).set_justify(Gtk::JUSTIFY_LEFT).set_wrap(true)
129
+
130
+ vbox.pack_start(lbl, true, true, 0)
131
+
132
+ append_page(vbox)
133
+ set_page_title(vbox, "Usage Rights Wizard")
134
+ set_page_type(vbox, Assistant::PAGE_INTRO)
135
+ set_page_complete(vbox, true)
136
+
137
+ end
138
+
139
+ def create_rights_selection_page
140
+
141
+ vbox = VBox.new(false, 5)
142
+
143
+ docframe = Frame.new(" Document ")
144
+ docframe.border_width = 5
145
+ docframe.shadow_type = Gtk::SHADOW_IN
146
+
147
+ doctable = Table.new(1, 2)
148
+ doctable.attach(@document_fullsave = CheckButton.new("Full Save").set_active(true), 0, 1, 0, 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
149
+ docframe.add(doctable)
150
+
151
+ annotsframe = Frame.new(" Annotations ")
152
+ annotsframe.border_width = 5
153
+ annotsframe.shadow_type = Gtk::SHADOW_IN
154
+
155
+ annotstable = Table.new(4,2)
156
+ annots =
157
+ [
158
+ [ @annots_create = CheckButton.new("Create").set_active(true), @annots_import = CheckButton.new("Import").set_active(true) ],
159
+ [ @annots_delete = CheckButton.new("Delete").set_active(true), @annots_export = CheckButton.new("Export").set_active(true) ],
160
+ [ @annots_modify = CheckButton.new("Modify").set_active(true), @annots_online = CheckButton.new("Online").set_active(true) ],
161
+ [ @annots_copy = CheckButton.new("Copy").set_active(true), @annots_sumview = CheckButton.new("Summary View").set_active(true) ]
162
+ ]
163
+
164
+ tt = Tooltips.new.enable
165
+ tt.set_tip(@annots_create, "test", "")
166
+
167
+ row = 0
168
+ annots.each do |col1, col2|
169
+
170
+ annotstable.attach(col1, 0, 1, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
171
+ annotstable.attach(col2, 1, 2, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
172
+
173
+ row = row.succ
174
+ end
175
+
176
+ annotsframe.add(annotstable)
177
+
178
+ formframe = Frame.new(" Forms ")
179
+ formframe.border_width = 5
180
+ formframe.shadow_type = Gtk::SHADOW_IN
181
+
182
+ formtable = Table.new(4,2)
183
+ forms =
184
+ [
185
+ [ @form_fillin = CheckButton.new("Fill in").set_active(true), @form_spawntemplate = CheckButton.new("Spawn template").set_active(true) ],
186
+ [ @form_import = CheckButton.new("Import").set_active(true), @form_barcode = CheckButton.new("Barcode plaintext").set_active(true) ],
187
+ [ @form_export = CheckButton.new("Export").set_active(true), @form_online = CheckButton.new("Online").set_active(true) ],
188
+ [ @form_submit = CheckButton.new("Submit stand-alone").set_active(true), nil ]
189
+ ]
190
+
191
+ row = 0
192
+ forms.each do |col1, col2|
193
+
194
+ formtable.attach(col1, 0, 1, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
195
+ formtable.attach(col2, 1, 2, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
196
+
197
+ row = row.succ
198
+ end
199
+
200
+ formframe.add(formtable)
201
+
202
+ signatureframe = Frame.new(" Signature ")
203
+ signatureframe.border_width = 5
204
+ signatureframe.shadow_type = Gtk::SHADOW_IN
205
+
206
+ signaturetable = Table.new(1, 2)
207
+ signaturetable.attach(@signature_modify = CheckButton.new("Modify").set_active(true), 0, 1, 0, 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
208
+ signatureframe.add(signaturetable)
209
+
210
+ efframe = Frame.new(" Embedded files ")
211
+ efframe.border_width = 5
212
+ efframe.shadow_type = Gtk::SHADOW_IN
213
+
214
+ eftable = Table.new(2,2)
215
+ efitems =
216
+ [
217
+ [ @ef_create = CheckButton.new("Create").set_active(true), @ef_modify = CheckButton.new("Modify").set_active(true) ],
218
+ [ @ef_delete = CheckButton.new("Delete").set_active(true), @ef_import = CheckButton.new("Import").set_active(true) ]
219
+ ]
220
+
221
+ row = 0
222
+ efitems.each do |col1, col2|
223
+
224
+ eftable.attach(col1, 0, 1, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
225
+ eftable.attach(col2, 1, 2, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
226
+
227
+ row = row.succ
228
+ end
229
+
230
+ efframe.add(eftable)
231
+
232
+ vbox.add(docframe)
233
+ vbox.add(annotsframe)
234
+ vbox.add(formframe)
235
+ vbox.add(signatureframe)
236
+ vbox.add(efframe)
237
+
238
+ append_page(vbox)
239
+ set_page_title(vbox, "Select Usage Rights to enable")
240
+ set_page_type(vbox, Assistant::PAGE_CONFIRM)
241
+ set_page_complete(vbox, true)
242
+
243
+ end
244
+
245
+ def create_termination_page
246
+
247
+ @lastpage = VBox.new(false, 5)
248
+
249
+ @msg_status = Label.new
250
+ @lastpage.pack_start(@msg_status, true, true, 0)
251
+
252
+ append_page(@lastpage)
253
+ set_page_title(@lastpage, "Usage Rights have not been enabled")
254
+ set_page_type(@lastpage, Assistant::PAGE_SUMMARY)
255
+
256
+ end
257
+
258
+ end
259
+
260
+ class SignWizard < Assistant
261
+
262
+ INTRO_PAGE = 0
263
+ KEY_SELECT_PAGE = 1
264
+ PKCS12_IMPORT_PAGE = 2
265
+ KEYPAIR_IMPORT_PAGE = 3
266
+ SIGNATURE_INFO_PAGE = 4
267
+ SIGNATURE_RESULT_PAGE = 5
268
+
269
+ def initialize(parent, pdf)
270
+
271
+ super()
272
+
273
+ @parent = parent
274
+
275
+ @pkey, @cert, @ca = nil, nil, []
276
+
277
+ create_intro_page
278
+ create_key_selection_page
279
+ create_pkcs12_import_page
280
+ create_keypair_import_page
281
+ create_signature_info_page
282
+ create_termination_page
283
+
284
+ set_forward_page_func { |current_page|
285
+ case current_page
286
+ when KEY_SELECT_PAGE
287
+ if @p12button.active? then PKCS12_IMPORT_PAGE else KEYPAIR_IMPORT_PAGE end
288
+
289
+ when PKCS12_IMPORT_PAGE, KEYPAIR_IMPORT_PAGE
290
+ SIGNATURE_INFO_PAGE
291
+
292
+ else current_page.succ
293
+ end
294
+ }
295
+
296
+ signal_connect('delete_event') { self.destroy }
297
+ signal_connect('cancel') { self.destroy }
298
+ signal_connect('close') { self.destroy }
299
+
300
+ signal_connect('apply') {
301
+
302
+ location = @location.text.empty? ? nil : @location.text
303
+ contact = @email.text.empty? ? nil : @email.text
304
+ reason = @reason.text.empty? ? nil : @reason.text
305
+
306
+ begin
307
+ pdf.sign(@cert, @pkey, @ca, nil, location, contact, reason)
308
+
309
+ set_page_title(@lastpage, "Document has been signed")
310
+ @msg_status.text = "The document has been signed.\n You should consider saving it now."
311
+
312
+ @parent.reload
313
+ rescue Exception => e
314
+ puts e
315
+ puts e.backtrace
316
+
317
+ set_page_title(@lastpage, "Document has not been signed")
318
+ @msg_status.text = "An error occured during the signature process."
319
+ end
320
+ }
321
+
322
+ show_all
323
+
324
+ end
325
+
326
+ private
327
+
328
+ def create_intro_page
329
+
330
+ intro = <<INTRO
331
+ You are about to sign the current PDF document.
332
+ Once the document will be signed, no further modification will be allowed.
333
+
334
+ The signature process is based on assymetric cryptography, so you will basically need a public/private RSA key pair (between 1024 and 4096 bits).
335
+ INTRO
336
+
337
+ vbox = VBox.new(false, 5)
338
+ vbox.set_border_width(5)
339
+
340
+ lbl = Label.new(intro).set_justify(Gtk::JUSTIFY_LEFT).set_wrap(true)
341
+
342
+ vbox.pack_start(lbl, true, true, 0)
343
+
344
+ append_page(vbox)
345
+ set_page_title(vbox, "Signature Wizard")
346
+ set_page_type(vbox, Assistant::PAGE_INTRO)
347
+ set_page_complete(vbox, true)
348
+
349
+ end
350
+
351
+ def create_key_selection_page
352
+
353
+ vbox = VBox.new(false, 5)
354
+
355
+ @p12button = RadioButton.new("Import keys from a PKCS12 container")
356
+ @rawbutton = RadioButton.new(@p12button, "Import keys from separate PEM/DER encoded files")
357
+
358
+ vbox.pack_start(@p12button, true, true, 0)
359
+ vbox.pack_start(@rawbutton, true, true, 0)
360
+
361
+ append_page(vbox)
362
+ set_page_title(vbox, "Choose a key importation method")
363
+ set_page_type(vbox, Assistant::PAGE_CONTENT)
364
+ set_page_complete(vbox, true)
365
+
366
+ end
367
+
368
+ def create_pkcs12_import_page
369
+
370
+ def get_passwd
371
+
372
+ dialog = Dialog.new("Enter passphrase",
373
+ @parent,
374
+ Dialog::MODAL,
375
+ [Stock::OK, Dialog::RESPONSE_OK]
376
+ )
377
+
378
+ pwd_entry = Entry.new.set_visibility(false).show
379
+
380
+ dialog.vbox.pack_start(pwd_entry, true, true, 0)
381
+
382
+ pwd = (dialog.run == Dialog::RESPONSE_OK) ? pwd_entry.text : ""
383
+
384
+ dialog.destroy
385
+
386
+ return pwd
387
+
388
+ end
389
+
390
+ def open_file_dialog(page)
391
+
392
+ dialog = FileChooserDialog.new("Open PKCS12 container",
393
+ @parent,
394
+ FileChooser::ACTION_OPEN,
395
+ nil,
396
+ [Stock::CANCEL, Dialog::RESPONSE_CANCEL],
397
+ [Stock::OPEN, Dialog::RESPONSE_ACCEPT])
398
+ filter = FileFilter.new
399
+ filter.add_pattern("*.pfx")
400
+ filter.add_pattern("*.p12")
401
+
402
+ dialog.filter = filter
403
+
404
+ if dialog.run == Dialog::RESPONSE_ACCEPT
405
+
406
+ begin
407
+ p12 = OpenSSL::PKCS12::PKCS12.new(File.open(dialog.filename, 'r').binmode.read, get_passwd)
408
+
409
+ if not p12.key.is_a?(OpenSSL::PKey::RSA) then raise TypeError end
410
+ if not p12.certificate.is_a?(OpenSSL::X509::Certificate) then raise TypeError end
411
+
412
+ @pkey = p12.key
413
+ @cert = p12.certificate
414
+ @ca = p12.ca_certs
415
+
416
+ @p12filename.set_text(dialog.filename)
417
+ set_page_complete(page, true)
418
+
419
+ rescue Exception => e
420
+ puts e.backtrace
421
+ error = MessageDialog.new(@parent,
422
+ Dialog::MODAL,
423
+ Gtk::MessageDialog::ERROR,
424
+ Gtk::MessageDialog::BUTTONS_CLOSE,
425
+ "Error loading file '#{File.basename(dialog.filename)}'")
426
+ error.run
427
+ error.destroy
428
+
429
+ @pkey, @cert, @ca = nil, nil, []
430
+ @p12filename.text = ""
431
+ set_page_complete(page, false)
432
+
433
+ end
434
+
435
+ end
436
+
437
+ dialog.destroy
438
+
439
+ end
440
+
441
+ vbox = VBox.new(false, 5)
442
+
443
+ hbox = HBox.new(false, 5)
444
+ vbox.pack_start(hbox, true, false, 10)
445
+
446
+ @p12filename = Entry.new.set_editable(false).set_sensitive(false)
447
+ choosebtn = Button.new(Gtk::Stock::OPEN)
448
+
449
+ choosebtn.signal_connect('clicked') { open_file_dialog(vbox) }
450
+
451
+ hbox.pack_start(@p12filename, true, true, 5)
452
+ hbox.pack_start(choosebtn, false, false, 5)
453
+
454
+ append_page(vbox)
455
+ set_page_title(vbox, "Import a PKCS12 container")
456
+ set_page_type(vbox, Assistant::PAGE_CONTENT)
457
+
458
+ end
459
+
460
+ def create_keypair_import_page
461
+
462
+ def open_pkey_dialog(page)
463
+
464
+ dialog = FileChooserDialog.new("Choose a private RSA key",
465
+ @parent,
466
+ FileChooser::ACTION_OPEN,
467
+ nil,
468
+ [Stock::CANCEL, Dialog::RESPONSE_CANCEL],
469
+ [Stock::OPEN, Dialog::RESPONSE_ACCEPT])
470
+ filter = FileFilter.new
471
+ filter.add_pattern("*.key")
472
+ filter.add_pattern("*.pem")
473
+ filter.add_pattern("*.der")
474
+
475
+ dialog.set_filter(filter)
476
+
477
+ if dialog.run == Dialog::RESPONSE_ACCEPT
478
+
479
+ begin
480
+ @pkey = OpenSSL::PKey::RSA.new(File.open(dialog.filename, 'r').binmode.read)
481
+
482
+ @pkeyfilename.set_text(dialog.filename)
483
+ if @cert then set_page_complete(page, true) end
484
+
485
+ rescue Exception => e
486
+ puts e.backtrace
487
+ error = MessageDialog.new(@parent,
488
+ Dialog::MODAL,
489
+ Gtk::MessageDialog::ERROR,
490
+ Gtk::MessageDialog::BUTTONS_CLOSE,
491
+ "Error loading file '#{File.basename(dialog.filename)}'")
492
+ error.run
493
+ error.destroy
494
+
495
+ @pkey = nil
496
+ @pkeyfilename.text = ""
497
+ set_page_complete(page, false)
498
+
499
+ ensure
500
+ @ca = [] # Shall be added to the GUI
501
+ end
502
+
503
+ end
504
+
505
+ dialog.destroy
506
+
507
+ end
508
+
509
+ def open_cert_dialog(page)
510
+
511
+ dialog = FileChooserDialog.new("Choose a private RSA key",
512
+ @parent,
513
+ FileChooser::ACTION_OPEN,
514
+ nil,
515
+ [Stock::CANCEL, Dialog::RESPONSE_CANCEL],
516
+ [Stock::OPEN, Dialog::RESPONSE_ACCEPT])
517
+ filter = FileFilter.new
518
+ filter.add_pattern("*.crt")
519
+ filter.add_pattern("*.cer")
520
+ filter.add_pattern("*.pem")
521
+ filter.add_pattern("*.der")
522
+
523
+ dialog.set_filter(filter)
524
+
525
+ if dialog.run == Dialog::RESPONSE_ACCEPT
526
+
527
+ begin
528
+ @cert = OpenSSL::X509::Certificate.new(File.open(dialog.filename, 'r').binmode.read)
529
+
530
+ @certfilename.set_text(dialog.filename)
531
+ if @pkey then set_page_complete(page, true) end
532
+
533
+ rescue Exception => e
534
+ puts e.backtrace
535
+ error = MessageDialog.new(@parent,
536
+ Dialog::MODAL,
537
+ Gtk::MessageDialog::ERROR,
538
+ Gtk::MessageDialog::BUTTONS_CLOSE,
539
+ "Error loading file '#{File.basename(dialog.filename)}'")
540
+ error.run
541
+ error.destroy
542
+
543
+ @cert = nil
544
+ @certfilename.text = ""
545
+ set_page_complete(page, false)
546
+
547
+ ensure
548
+ @ca = [] # Shall be added to the GUI
549
+ end
550
+
551
+ end
552
+
553
+ dialog.destroy
554
+
555
+ end
556
+
557
+ labels =
558
+ [
559
+ [ "Private RSA key:", @pkeyfilename = Entry.new, pkeychoosebtn = Button.new(Gtk::Stock::OPEN) ],
560
+ [ "Public certificate:", @certfilename = Entry.new, certchoosebtn = Button.new(Gtk::Stock::OPEN) ]
561
+ ]
562
+
563
+ row = 0
564
+ table = Table.new(2, 3)
565
+ labels.each do |lbl, entry, btn|
566
+
567
+ entry.editable = entry.sensitive = false
568
+
569
+ table.attach(Label.new(lbl).set_alignment(1,0), 0, 1, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
570
+ table.attach(entry, 1, 2, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
571
+ table.attach(btn, 2, 3, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
572
+
573
+ row = row.succ
574
+ end
575
+
576
+ pkeychoosebtn.signal_connect('clicked') { open_pkey_dialog(table) }
577
+ certchoosebtn.signal_connect('clicked') { open_cert_dialog(table) }
578
+
579
+ append_page(table)
580
+ set_page_title(table, "Import a public/private key pair")
581
+ set_page_type(table, Assistant::PAGE_CONTENT)
582
+
583
+ end
584
+
585
+ def create_signature_info_page
586
+
587
+ vbox = VBox.new(false, 5)
588
+
589
+ lbl = Label.new("Here are a few optional information you can add with your signature.")
590
+ vbox.pack_start(lbl, true, true, 0)
591
+
592
+ labels =
593
+ [
594
+ [ "Location:", @location = Entry.new ],
595
+ [ "Contact:", @email = Entry.new ],
596
+ [ "Reason:", @reason = Entry.new ]
597
+ ]
598
+
599
+ row = 0
600
+ table = Table.new(4, 3)
601
+ labels.each do |label|
602
+
603
+ table.attach(Label.new(label[0]).set_alignment(1,0), 0, 1, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
604
+ table.attach(label[1], 1, 2, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
605
+
606
+ row = row.succ
607
+ end
608
+
609
+ vbox.pack_start(table, true, true, 0)
610
+
611
+ append_page(vbox)
612
+ set_page_title(vbox, "Fill in signature details")
613
+ set_page_type(vbox, Assistant::PAGE_CONFIRM)
614
+ set_page_complete(vbox, true)
615
+
616
+ end
617
+
618
+ def create_termination_page
619
+
620
+ @lastpage = VBox.new(false, 5)
621
+
622
+ @msg_status = Label.new
623
+ @lastpage.pack_start(@msg_status, true, true, 0)
624
+
625
+ append_page(@lastpage)
626
+ set_page_title(@lastpage, "Document has not been signed")
627
+ set_page_type(@lastpage, Assistant::PAGE_SUMMARY)
628
+
629
+ end
630
+
631
+ end
632
+
633
+ end
634
+
635
+ end