pdfwalker 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,126 @@
1
+ =begin
2
+
3
+ This file is part of PDF Walker, a graphical PDF file browser
4
+ Copyright (C) 2017 Guillaume Delugré.
5
+
6
+ PDF Walker is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ PDF Walker is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU General Public License for more details.
15
+
16
+ You should have received a copy of the GNU General Public License
17
+ along with PDF Walker. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ =end
20
+
21
+ require 'digest/md5'
22
+
23
+ module PDFWalker
24
+
25
+ class Walker < Window
26
+
27
+ def display_file_properties
28
+ Properties.new(self, @opened) if @opened
29
+ end
30
+
31
+ class Properties < Dialog
32
+
33
+ @@acrobat_versions =
34
+ {
35
+ 1.0 => "1.x",
36
+ 1.1 => "2.x",
37
+ 1.2 => "3.x",
38
+ 1.3 => "4.x",
39
+ 1.4 => "5.x",
40
+ 1.5 => "6.x",
41
+ 1.6 => "7.x",
42
+ 1.7 => "8.x / 9.x / 10.x"
43
+ }
44
+
45
+ def initialize(parent, pdf)
46
+ super("Document properties", parent, Dialog::MODAL, [Stock::CLOSE, Dialog::RESPONSE_NONE])
47
+
48
+ file_frame = create_file_frame(parent)
49
+ pdf_frame = create_document_frame(pdf)
50
+
51
+ vbox.add(file_frame)
52
+ vbox.add(pdf_frame)
53
+
54
+ signal_connect('response') { destroy }
55
+
56
+ show_all
57
+ end
58
+
59
+ private
60
+
61
+ def create_file_frame(parent)
62
+ file_frame = Frame.new(" File properties ")
63
+ stat = File.stat(parent.filename)
64
+
65
+ labels =
66
+ [
67
+ [ "Filename:", parent.filename ],
68
+ [ "File size:", "#{File.size(parent.filename)} bytes" ],
69
+ [ "MD5:", Digest::MD5.file(parent.filename).hexdigest ],
70
+ [ "Read-only:", "#{not stat.writable?}" ],
71
+ [ "Creation date:", stat.ctime.to_s ],
72
+ [ "Last modified:", stat.mtime.to_s ]
73
+ ]
74
+
75
+ create_table(file_frame, labels)
76
+ end
77
+
78
+ def create_document_frame(pdf)
79
+ pdf_frame = Frame.new(" PDF properties ")
80
+
81
+ pdf_version = pdf.header.to_f
82
+ if pdf_version >= 1.0 and pdf_version <= 1.7
83
+ acrobat_version = @@acrobat_versions[pdf_version]
84
+ else
85
+ acrobat_version = "unknown version"
86
+ end
87
+
88
+ labels =
89
+ [
90
+ [ "Version:", "#{pdf_version} (Acrobat #{acrobat_version})" ],
91
+ [ "Number of revisions:", "#{pdf.revisions.size}" ],
92
+ [ "Number of indirect objects:", "#{pdf.indirect_objects.size}" ],
93
+ [ "Number of pages:", "#{pdf.pages.count}" ],
94
+ [ "Linearized:", boolean_text(pdf.linearized?) ],
95
+ [ "Encrypted:", boolean_text(pdf.encrypted?) ],
96
+ [ "Signed:", boolean_text(pdf.signed?) ],
97
+ [ "Has usage rights:", boolean_text(pdf.usage_rights?) ],
98
+ [ "Form:", boolean_text(pdf.form?) ],
99
+ [ "XFA form:", boolean_text(pdf.xfa_form?) ],
100
+ [ "Document information:", boolean_text(pdf.document_info?) ],
101
+ [ "Metadata:", boolean_text(pdf.metadata?) ]
102
+ ]
103
+
104
+ create_table(pdf_frame, labels)
105
+ end
106
+
107
+ def create_table(frame, labels)
108
+ table = Table.new(labels.size + 1, 3)
109
+
110
+ labels.each_with_index do |label, row|
111
+ table.attach(Label.new(label[0]).set_alignment(1,0), 0, 1, row, row + 1, Gtk::FILL, Gtk::SHRINK, 4, 4)
112
+ table.attach(Label.new(label[1]).set_alignment(0,0), 1, 2, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
113
+ end
114
+
115
+ frame.border_width = 5
116
+ frame.shadow_type = Gtk::SHADOW_IN
117
+ frame.add(table)
118
+ end
119
+
120
+ def boolean_text(value)
121
+ value ? 'yes' : 'no'
122
+ end
123
+ end
124
+ end
125
+
126
+ end
@@ -0,0 +1,558 @@
1
+ =begin
2
+
3
+ This file is part of PDF Walker, a graphical PDF file browser
4
+ Copyright (C) 2017 Guillaume Delugré.
5
+
6
+ PDF Walker is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ PDF Walker is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU General Public License for more details.
15
+
16
+ You should have received a copy of the GNU General Public License
17
+ along with PDF Walker. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ =end
20
+
21
+ module PDFWalker
22
+
23
+ class Walker < Window
24
+
25
+ def display_signing_wizard
26
+ SignWizard.new(self, @opened) if @opened
27
+ end
28
+
29
+ def display_usage_rights_wizard
30
+ UsageRightsWizard.new(self, @opened) if @opened
31
+ end
32
+
33
+ module SignatureDialogs
34
+ private
35
+
36
+ def open_private_key_dialog(page)
37
+ file_chooser_dialog('Choose a private RSA key', '*.key', '*.pem', '*.der') do |dialog|
38
+ begin
39
+ @pkey = OpenSSL::PKey::RSA.new(File.binread(dialog.filename))
40
+
41
+ @pkeyfilename.set_text(dialog.filename)
42
+ set_page_complete(page, true) if @cert
43
+ rescue
44
+ @parent.error("Error loading file '#{File.basename(dialog.filename)}'")
45
+
46
+ @pkey = nil
47
+ @pkeyfilename.text = ""
48
+ set_page_complete(page, false)
49
+ ensure
50
+ @ca = [] # Shall be added to the GUI
51
+ end
52
+ end
53
+ end
54
+
55
+ def open_certificate_dialog(page)
56
+ file_chooser_dialog('Choose a x509 certificate', '*.crt', '*.cer', '*.pem', '*.der') do |dialog|
57
+ begin
58
+ @cert = OpenSSL::X509::Certificate.new(File.binread(dialog.filename))
59
+
60
+ @certfilename.set_text(dialog.filename)
61
+ set_page_complete(page, true) if @pkey
62
+
63
+ rescue
64
+ @parent.error("Error loading file '#{File.basename(dialog.filename)}'")
65
+
66
+ @cert = nil
67
+ @certfilename.text = ""
68
+ set_page_complete(page, false)
69
+ ensure
70
+ @ca = [] # Shall be added to the GUI
71
+ end
72
+ end
73
+ end
74
+
75
+ def open_pkcs12_file_dialog(page)
76
+
77
+ file_chooser_dialog('Open PKCS12 container', '*.pfx', '*.p12') do |dialog|
78
+ begin
79
+ p12 = OpenSSL::PKCS12::PKCS12.new(File.binread(dialog.filename), method(:prompt_passphrase))
80
+
81
+ raise TypeError, "PKCS12 does not contain a RSA key" unless p12.key.is_a?(OpenSSL::PKey::RSA)
82
+ raise TypeError, "PKCS12 does not contain a x509 certificate" unless p12.certificate.is_a?(OpenSSL::X509::Certificate)
83
+
84
+ @pkey = p12.key
85
+ @cert = p12.certificate
86
+ @ca = p12.ca_certs
87
+
88
+ @p12filename.set_text(dialog.filename)
89
+ set_page_complete(page, true)
90
+ rescue
91
+ @parent.error("Error loading file '#{File.basename(dialog.filename)}'")
92
+
93
+ @pkey, @cert, @ca = nil, nil, []
94
+ @p12filename.text = ""
95
+ set_page_complete(page, false)
96
+ end
97
+ end
98
+ end
99
+
100
+ def create_keypair_import_page
101
+ labels =
102
+ [
103
+ [ "Private RSA key:", @pkeyfilename = Entry.new, pkeychoosebtn = Button.new(Gtk::Stock::OPEN) ],
104
+ [ "Public certificate:", @certfilename = Entry.new, certchoosebtn = Button.new(Gtk::Stock::OPEN) ]
105
+ ]
106
+
107
+ row = 0
108
+ table = Table.new(2, 3)
109
+ labels.each do |lbl, entry, btn|
110
+ entry.editable = entry.sensitive = false
111
+
112
+ table.attach(Label.new(lbl).set_alignment(1,0), 0, 1, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
113
+ table.attach(entry, 1, 2, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
114
+ table.attach(btn, 2, 3, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
115
+
116
+ row = row.succ
117
+ end
118
+
119
+ pkeychoosebtn.signal_connect('clicked') { open_private_key_dialog(table) }
120
+ certchoosebtn.signal_connect('clicked') { open_certificate_dialog(table) }
121
+
122
+ append_page(table)
123
+ set_page_title(table, "Import a public/private key pair")
124
+ set_page_type(table, Assistant::PAGE_CONTENT)
125
+ end
126
+
127
+ def prompt_passphrase
128
+ dialog = Dialog.new("Enter passphrase",
129
+ @parent,
130
+ Dialog::MODAL,
131
+ [Stock::OK, Dialog::RESPONSE_OK]
132
+ )
133
+
134
+ pwd_entry = Entry.new.set_visibility(false).show
135
+ dialog.vbox.pack_start(pwd_entry, true, true, 0)
136
+
137
+ pwd = pwd_entry.text if dialog.run == Dialog::RESPONSE_OK
138
+
139
+ dialog.destroy
140
+ pwd.to_s
141
+ end
142
+
143
+ def file_chooser_dialog(title, *patterns)
144
+ dialog = FileChooserDialog.new(title,
145
+ @parent,
146
+ FileChooser::ACTION_OPEN,
147
+ nil,
148
+ [Stock::CANCEL, Dialog::RESPONSE_CANCEL],
149
+ [Stock::OPEN, Dialog::RESPONSE_ACCEPT])
150
+
151
+ filter = FileFilter.new
152
+ patterns.each do |pattern|
153
+ filter.add_pattern(pattern)
154
+ end
155
+
156
+ dialog.set_filter(filter)
157
+
158
+ if dialog.run == Dialog::RESPONSE_ACCEPT
159
+ yield(dialog)
160
+ end
161
+
162
+ dialog.destroy
163
+ end
164
+ end
165
+
166
+ class UsageRightsWizard < Assistant
167
+ include SignatureDialogs
168
+
169
+ def initialize(parent, pdf)
170
+ super()
171
+
172
+ @parent = parent
173
+ @pkey, @cert = nil, nil
174
+
175
+ create_intro_page
176
+ create_keypair_import_page
177
+ create_rights_selection_page
178
+ create_termination_page
179
+
180
+ signal_connect('delete_event') { self.destroy }
181
+ signal_connect('cancel') { self.destroy }
182
+ signal_connect('close') { self.destroy }
183
+
184
+ signal_connect('apply') {
185
+ rights = selected_usage_rights
186
+
187
+ begin
188
+ pdf.enable_usage_rights(@cert, @pkey, *rights)
189
+
190
+ set_page_title(@lastpage, "Usage Rights have been enabled")
191
+ @msg_status.text = "Usage Rights have been enabled for the current document.\n You should consider saving it now."
192
+
193
+ @parent.reload
194
+ rescue
195
+ @parent.error("#{$!}: #{$!.backtrace.join($/)}")
196
+
197
+ set_page_title(@lastpage, "Usage Rights have not been enabled")
198
+ @msg_status.text = "An error occured during the signature process."
199
+ end
200
+ }
201
+
202
+ set_modal(true)
203
+
204
+ show_all
205
+ end
206
+
207
+ private
208
+
209
+ def selected_usage_rights
210
+ [
211
+ [ Origami::UsageRights::Rights::DOCUMENT_FULLSAVE, @document_fullsave ],
212
+
213
+ [ Origami::UsageRights::Rights::ANNOTS_CREATE, @annots_create ],
214
+ [ Origami::UsageRights::Rights::ANNOTS_DELETE, @annots_delete ],
215
+ [ Origami::UsageRights::Rights::ANNOTS_MODIFY, @annots_modify ],
216
+ [ Origami::UsageRights::Rights::ANNOTS_COPY, @annots_copy ],
217
+ [ Origami::UsageRights::Rights::ANNOTS_IMPORT, @annots_import ],
218
+ [ Origami::UsageRights::Rights::ANNOTS_EXPORT, @annots_export ],
219
+ [ Origami::UsageRights::Rights::ANNOTS_ONLINE, @annots_online ],
220
+ [ Origami::UsageRights::Rights::ANNOTS_SUMMARYVIEW, @annots_sumview ],
221
+
222
+ [ Origami::UsageRights::Rights::FORM_FILLIN, @form_fillin ],
223
+ [ Origami::UsageRights::Rights::FORM_IMPORT, @form_import ],
224
+ [ Origami::UsageRights::Rights::FORM_EXPORT, @form_export ],
225
+ [ Origami::UsageRights::Rights::FORM_SUBMITSTANDALONE, @form_submit ],
226
+ [ Origami::UsageRights::Rights::FORM_SPAWNTEMPLATE, @form_spawntemplate ],
227
+ [ Origami::UsageRights::Rights::FORM_BARCODEPLAINTEXT, @form_barcode ],
228
+ [ Origami::UsageRights::Rights::FORM_ONLINE, @form_online ],
229
+
230
+ [ Origami::UsageRights::Rights::SIGNATURE_MODIFY, @signature_modify ],
231
+
232
+ [ Origami::UsageRights::Rights::EF_CREATE, @ef_create ],
233
+ [ Origami::UsageRights::Rights::EF_DELETE, @ef_delete ],
234
+ [ Origami::UsageRights::Rights::EF_MODIFY, @ef_modify ],
235
+ [ Origami::UsageRights::Rights::EF_IMPORT, @ef_import ],
236
+ ].select { |_, button| button.active? }
237
+ .map { |right, _| right }
238
+ end
239
+
240
+ def create_intro_page
241
+ intro = <<-INTRO.gsub(/^\s+/, '')
242
+ You are about to enable Usage Rights for the current PDF document.
243
+ To enable these features, you need to have an Adobe public/private key pair in your possession.
244
+
245
+ Make sure you have adobe.crt and adobe.key located in the current directory.
246
+ INTRO
247
+
248
+ vbox = VBox.new(false, 5)
249
+ vbox.set_border_width(5)
250
+
251
+ lbl = Label.new(intro).set_justify(Gtk::JUSTIFY_LEFT).set_wrap(true)
252
+
253
+ vbox.pack_start(lbl, true, true, 0)
254
+
255
+ append_page(vbox)
256
+ set_page_title(vbox, "Usage Rights Wizard")
257
+ set_page_type(vbox, Assistant::PAGE_INTRO)
258
+ set_page_complete(vbox, true)
259
+ end
260
+
261
+ def create_rights_frame(name)
262
+ frame = Frame.new(name)
263
+ frame.border_width = 5
264
+ frame.shadow_type = Gtk::SHADOW_IN
265
+
266
+ frame
267
+ end
268
+
269
+ def create_document_rights_frame
270
+ frame = create_rights_frame(" Document ")
271
+
272
+ @document_fullsave = CheckButton.new("Full Save").set_active(true)
273
+
274
+ doc_table = Table.new(1, 2)
275
+ doc_table.attach(@document_fullsave, 0, 1, 0, 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
276
+ frame.add(doc_table)
277
+ end
278
+
279
+ def create_annotations_rights_frame
280
+ frame = create_rights_frame(" Annotations ")
281
+
282
+ annots_table = Table.new(4, 2)
283
+ annots =
284
+ [
285
+ [ @annots_create = CheckButton.new("Create"), @annots_import = CheckButton.new("Import") ],
286
+ [ @annots_delete = CheckButton.new("Delete"), @annots_export = CheckButton.new("Export") ],
287
+ [ @annots_modify = CheckButton.new("Modify"), @annots_online = CheckButton.new("Online") ],
288
+ [ @annots_copy = CheckButton.new("Copy"), @annots_sumview = CheckButton.new("Summary View") ]
289
+ ]
290
+
291
+ annots.each_with_index do |cols, row|
292
+ col1, col2 = cols
293
+
294
+ col1.active = true
295
+ col2.active = true
296
+
297
+ annots_table.attach(col1, 0, 1, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
298
+ annots_table.attach(col2, 1, 2, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
299
+ end
300
+
301
+ frame.add(annots_table)
302
+ end
303
+
304
+ def create_form_rights_frame
305
+ frame = create_rights_frame(" Forms ")
306
+
307
+ form_table = Table.new(4, 2)
308
+ forms =
309
+ [
310
+ [ @form_fillin = CheckButton.new("Fill in"), @form_spawntemplate = CheckButton.new("Spawn template") ],
311
+ [ @form_import = CheckButton.new("Import"), @form_barcode = CheckButton.new("Barcode plaintext") ],
312
+ [ @form_export = CheckButton.new("Export"), @form_online = CheckButton.new("Online") ],
313
+ [ @form_submit = CheckButton.new("Submit stand-alone"), nil ]
314
+ ]
315
+
316
+ forms.each_with_index do |cols, row|
317
+ col1, col2 = cols
318
+
319
+ col1.active = true
320
+ col2.active = true unless col2.nil?
321
+
322
+ form_table.attach(col1, 0, 1, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
323
+ form_table.attach(col2, 1, 2, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4) unless col2.nil?
324
+ end
325
+
326
+ frame.add(form_table)
327
+ end
328
+
329
+ def create_signature_rights_frame
330
+ frame = create_rights_frame(" Signature ")
331
+
332
+ @signature_modify = CheckButton.new("Modify").set_active(true)
333
+
334
+ signature_table = Table.new(1, 2)
335
+ signature_table.attach(@signature_modify, 0, 1, 0, 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
336
+ frame.add(signature_table)
337
+ end
338
+
339
+ def create_embedded_files_rights_frame
340
+ frame = create_rights_frame(" Embedded files ")
341
+
342
+ ef_table = Table.new(2,2)
343
+ ef_buttons =
344
+ [
345
+ [ @ef_create = CheckButton.new("Create"), @ef_modify = CheckButton.new("Modify") ],
346
+ [ @ef_delete = CheckButton.new("Delete"), @ef_import = CheckButton.new("Import") ]
347
+ ]
348
+
349
+ ef_buttons.each_with_index do |cols, row|
350
+ col1, col2 = cols
351
+
352
+ col1.active = true
353
+ col2.active = true
354
+
355
+ ef_table.attach(col1, 0, 1, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
356
+ ef_table.attach(col2, 1, 2, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
357
+ end
358
+
359
+ frame.add(ef_table)
360
+ end
361
+
362
+ def create_rights_selection_page
363
+ vbox = VBox.new(false, 5)
364
+
365
+ vbox.add create_document_rights_frame
366
+ vbox.add create_annotations_rights_frame
367
+ vbox.add create_form_rights_frame
368
+ vbox.add create_signature_rights_frame
369
+ vbox.add create_embedded_files_rights_frame
370
+
371
+ append_page(vbox)
372
+ set_page_title(vbox, "Select Usage Rights to enable")
373
+ set_page_type(vbox, Assistant::PAGE_CONFIRM)
374
+ set_page_complete(vbox, true)
375
+ end
376
+
377
+ def create_termination_page
378
+ @lastpage = VBox.new(false, 5)
379
+
380
+ @msg_status = Label.new
381
+ @lastpage.pack_start(@msg_status, true, true, 0)
382
+
383
+ append_page(@lastpage)
384
+ set_page_title(@lastpage, "Usage Rights have not been enabled")
385
+ set_page_type(@lastpage, Assistant::PAGE_SUMMARY)
386
+ end
387
+ end
388
+
389
+ class SignWizard < Assistant
390
+ include SignatureDialogs
391
+
392
+ INTRO_PAGE = 0
393
+ KEY_SELECT_PAGE = 1
394
+ PKCS12_IMPORT_PAGE = 2
395
+ KEYPAIR_IMPORT_PAGE = 3
396
+ SIGNATURE_INFO_PAGE = 4
397
+ SIGNATURE_RESULT_PAGE = 5
398
+
399
+ def initialize(parent, pdf)
400
+ super()
401
+
402
+ @parent = parent
403
+
404
+ @pkey, @cert, @ca = nil, nil, []
405
+
406
+ create_intro_page
407
+ create_key_selection_page
408
+ create_pkcs12_import_page
409
+ create_keypair_import_page
410
+ create_signature_info_page
411
+ create_termination_page
412
+
413
+ set_forward_page_func { |current_page|
414
+ case current_page
415
+ when KEY_SELECT_PAGE
416
+ if @p12button.active? then PKCS12_IMPORT_PAGE else KEYPAIR_IMPORT_PAGE end
417
+
418
+ when PKCS12_IMPORT_PAGE, KEYPAIR_IMPORT_PAGE
419
+ SIGNATURE_INFO_PAGE
420
+
421
+ else current_page.succ
422
+ end
423
+ }
424
+
425
+ signal_connect('delete_event') { self.destroy }
426
+ signal_connect('cancel') { self.destroy }
427
+ signal_connect('close') { self.destroy }
428
+
429
+ signal_connect('apply') {
430
+ location = @location.text.empty? ? nil : @location.text
431
+ contact = @email.text.empty? ? nil : @email.text
432
+ reason = @reason.text.empty? ? nil : @reason.text
433
+
434
+ begin
435
+ pdf.sign(@cert, @pkey,
436
+ ca: @ca,
437
+ location: location,
438
+ contact: contact,
439
+ reason: reason)
440
+
441
+ set_page_title(@lastpage, "Document has been signed")
442
+ @msg_status.text = "The document has been signed.\n You should consider saving it now."
443
+
444
+ @parent.reload
445
+ rescue
446
+ @parent.error("#{$!}: #{$!.backtrace.join($/)}")
447
+
448
+ set_page_title(@lastpage, "Document has not been signed")
449
+ @msg_status.text = "An error occured during the signature process."
450
+ end
451
+ }
452
+
453
+ set_modal(true)
454
+
455
+ show_all
456
+ end
457
+
458
+ private
459
+
460
+ def create_intro_page
461
+ intro = <<-INTRO.gsub(/^\s+/, '')
462
+ You are about to sign the current PDF document.
463
+ Once the document will be signed, no further modification will be allowed.
464
+
465
+ The signature process is based on assymetric cryptography, so you will basically need a public/private RSA key pair (between 1024 and 4096 bits).
466
+ INTRO
467
+
468
+ vbox = VBox.new(false, 5)
469
+ vbox.set_border_width(5)
470
+
471
+ lbl = Label.new(intro).set_justify(Gtk::JUSTIFY_LEFT).set_wrap(true)
472
+
473
+ vbox.pack_start(lbl, true, true, 0)
474
+
475
+ append_page(vbox)
476
+ set_page_title(vbox, "Signature Wizard")
477
+ set_page_type(vbox, Assistant::PAGE_INTRO)
478
+ set_page_complete(vbox, true)
479
+ end
480
+
481
+ def create_key_selection_page
482
+ vbox = VBox.new(false, 5)
483
+
484
+ @rawbutton = RadioButton.new("Import keys from separate PEM/DER encoded files")
485
+ @p12button = RadioButton.new(@rawbutton, "Import keys from a PKCS12 container")
486
+
487
+ vbox.pack_start(@rawbutton, true, true, 0)
488
+ vbox.pack_start(@p12button, true, true, 0)
489
+
490
+ append_page(vbox)
491
+ set_page_title(vbox, "Choose a key importation method")
492
+ set_page_type(vbox, Assistant::PAGE_CONTENT)
493
+ set_page_complete(vbox, true)
494
+ end
495
+
496
+ def create_pkcs12_import_page
497
+ vbox = VBox.new(false, 5)
498
+
499
+ hbox = HBox.new(false, 5)
500
+ vbox.pack_start(hbox, true, false, 10)
501
+
502
+ @p12filename = Entry.new.set_editable(false).set_sensitive(false)
503
+ choosebtn = Button.new(Gtk::Stock::OPEN)
504
+
505
+ choosebtn.signal_connect('clicked') { open_pkcs12_file_dialog(vbox) }
506
+
507
+ hbox.pack_start(@p12filename, true, true, 5)
508
+ hbox.pack_start(choosebtn, false, false, 5)
509
+
510
+ append_page(vbox)
511
+ set_page_title(vbox, "Import a PKCS12 container")
512
+ set_page_type(vbox, Assistant::PAGE_CONTENT)
513
+ end
514
+
515
+ def create_signature_info_page
516
+ vbox = VBox.new(false, 5)
517
+
518
+ lbl = Label.new("Here are a few optional information you can add with your signature.")
519
+ vbox.pack_start(lbl, true, true, 0)
520
+
521
+ labels =
522
+ [
523
+ [ "Location:", @location = Entry.new ],
524
+ [ "Contact:", @email = Entry.new ],
525
+ [ "Reason:", @reason = Entry.new ]
526
+ ]
527
+
528
+ row = 0
529
+ table = Table.new(4, 3)
530
+ labels.each do |label|
531
+ table.attach(Label.new(label[0]).set_alignment(1,0), 0, 1, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
532
+ table.attach(label[1], 1, 2, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
533
+
534
+ row = row.succ
535
+ end
536
+
537
+ vbox.pack_start(table, true, true, 0)
538
+
539
+ append_page(vbox)
540
+ set_page_title(vbox, "Fill in signature details")
541
+ set_page_type(vbox, Assistant::PAGE_CONFIRM)
542
+ set_page_complete(vbox, true)
543
+ end
544
+
545
+ def create_termination_page
546
+ @lastpage = VBox.new(false, 5)
547
+
548
+ @msg_status = Label.new
549
+ @lastpage.pack_start(@msg_status, true, true, 0)
550
+
551
+ append_page(@lastpage)
552
+ set_page_title(@lastpage, "Document has not been signed")
553
+ set_page_type(@lastpage, Assistant::PAGE_SUMMARY)
554
+ end
555
+ end
556
+ end
557
+
558
+ end