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,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