1 | #include "gl-app.h" |
2 | |
3 | #include <string.h> |
4 | #include <stdio.h> |
5 | |
6 | #ifndef PATH_MAX |
7 | #define PATH_MAX 2048 |
8 | #endif |
9 | |
10 | #include "mupdf/helpers/pkcs7-check.h" |
11 | #include "mupdf/helpers/pkcs7-openssl.h" |
12 | |
13 | static pdf_widget *sig_widget; |
14 | static char sig_designated_name[500]; |
15 | static enum pdf_signature_error sig_cert_error; |
16 | static enum pdf_signature_error sig_digest_error; |
17 | static int sig_subsequent_edits; |
18 | |
19 | static char cert_filename[PATH_MAX]; |
20 | static struct input cert_password; |
21 | |
22 | static void do_sign(void) |
23 | { |
24 | pdf_pkcs7_signer *signer = NULL; |
25 | |
26 | fz_var(signer); |
27 | |
28 | fz_try(ctx) |
29 | { |
30 | signer = pkcs7_openssl_read_pfx(ctx, cert_filename, cert_password.text); |
31 | pdf_sign_signature(ctx, pdf, sig_widget, signer); |
32 | ui_show_warning_dialog("Signed document successfully." ); |
33 | } |
34 | fz_always(ctx) |
35 | { |
36 | if (signer) |
37 | signer->drop(signer); |
38 | } |
39 | fz_catch(ctx) |
40 | ui_show_warning_dialog("%s" , fz_caught_message(ctx)); |
41 | |
42 | if (pdf_update_page(ctx, sig_widget->page)) |
43 | render_page(); |
44 | } |
45 | |
46 | static void do_clear_signature(void) |
47 | { |
48 | fz_try(ctx) |
49 | { |
50 | pdf_clear_signature(ctx, pdf, sig_widget); |
51 | ui_show_warning_dialog("Signature cleared successfully." ); |
52 | } |
53 | fz_catch(ctx) |
54 | ui_show_warning_dialog("%s" , fz_caught_message(ctx)); |
55 | |
56 | if (pdf_update_page(ctx, sig_widget->page)) |
57 | render_page(); |
58 | } |
59 | |
60 | static void cert_password_dialog(void) |
61 | { |
62 | int is; |
63 | ui_dialog_begin(400, (ui.gridsize+4)*3); |
64 | { |
65 | ui_layout(T, X, NW, 2, 2); |
66 | ui_label("Password:" ); |
67 | is = ui_input(&cert_password, 200, 1); |
68 | |
69 | ui_layout(B, X, NW, 2, 2); |
70 | ui_panel_begin(0, ui.gridsize, 0, 0, 0); |
71 | { |
72 | ui_layout(R, NONE, S, 0, 0); |
73 | if (ui_button("Cancel" )) |
74 | ui.dialog = NULL; |
75 | ui_spacer(); |
76 | if (ui_button("Okay" ) || is == UI_INPUT_ACCEPT) |
77 | { |
78 | ui.dialog = NULL; |
79 | do_sign(); |
80 | } |
81 | } |
82 | ui_panel_end(); |
83 | } |
84 | ui_dialog_end(); |
85 | } |
86 | |
87 | static int cert_file_filter(const char *fn) |
88 | { |
89 | return !!strstr(fn, ".pfx" ); |
90 | } |
91 | |
92 | static void cert_file_dialog(void) |
93 | { |
94 | if (ui_open_file(cert_filename)) |
95 | { |
96 | if (cert_filename[0] != 0) |
97 | { |
98 | ui_input_init(&cert_password, "" ); |
99 | ui.focus = &cert_password; |
100 | ui.dialog = cert_password_dialog; |
101 | } |
102 | else |
103 | ui.dialog = NULL; |
104 | } |
105 | } |
106 | |
107 | static void sig_sign_dialog(void) |
108 | { |
109 | const char *label = pdf_field_label(ctx, sig_widget->obj); |
110 | |
111 | ui_dialog_begin(400, (ui.gridsize+4)*3 + ui.lineheight*10); |
112 | { |
113 | ui_layout(T, X, NW, 2, 2); |
114 | |
115 | ui_label("%s" , label); |
116 | ui_spacer(); |
117 | |
118 | ui_label("Would you like to sign this field?" ); |
119 | |
120 | ui_layout(B, X, NW, 2, 2); |
121 | ui_panel_begin(0, ui.gridsize, 0, 0, 0); |
122 | { |
123 | ui_layout(R, NONE, S, 0, 0); |
124 | if (ui_button("Cancel" ) || (!ui.focus && ui.key == KEY_ESCAPE)) |
125 | ui.dialog = NULL; |
126 | ui_spacer(); |
127 | if (!(pdf_field_flags(ctx, sig_widget->obj) & PDF_FIELD_IS_READ_ONLY)) |
128 | { |
129 | if (ui_button("Sign" )) |
130 | { |
131 | fz_strlcpy(cert_filename, filename, sizeof cert_filename); |
132 | ui_init_open_file("." , cert_file_filter); |
133 | ui.dialog = cert_file_dialog; |
134 | } |
135 | } |
136 | } |
137 | ui_panel_end(); |
138 | } |
139 | ui_dialog_end(); |
140 | } |
141 | |
142 | static void sig_verify_dialog(void) |
143 | { |
144 | const char *label = pdf_field_label(ctx, sig_widget->obj); |
145 | |
146 | ui_dialog_begin(400, (ui.gridsize+4)*3 + ui.lineheight*10); |
147 | { |
148 | ui_layout(T, X, NW, 2, 2); |
149 | |
150 | ui_label("%s" , label); |
151 | ui_spacer(); |
152 | |
153 | ui_label("Designated name: %s." , sig_designated_name); |
154 | ui_spacer(); |
155 | |
156 | if (sig_cert_error) |
157 | ui_label("Certificate error: %s" , pdf_signature_error_description(sig_cert_error)); |
158 | else |
159 | ui_label("Certificate is trusted." ); |
160 | |
161 | ui_spacer(); |
162 | |
163 | if (sig_digest_error) |
164 | ui_label("Digest error: %s" , pdf_signature_error_description(sig_digest_error)); |
165 | else if (sig_subsequent_edits) |
166 | ui_label("The signature is valid but there have been edits since signing." ); |
167 | else |
168 | ui_label("The document is unchanged since signing." ); |
169 | |
170 | ui_layout(B, X, NW, 2, 2); |
171 | ui_panel_begin(0, ui.gridsize, 0, 0, 0); |
172 | { |
173 | ui_layout(L, NONE, S, 0, 0); |
174 | if (ui_button("Clear" )) |
175 | { |
176 | ui.dialog = NULL; |
177 | do_clear_signature(); |
178 | } |
179 | ui_layout(R, NONE, S, 0, 0); |
180 | if (ui_button("Close" ) || (!ui.focus && ui.key == KEY_ESCAPE)) |
181 | ui.dialog = NULL; |
182 | } |
183 | ui_panel_end(); |
184 | } |
185 | ui_dialog_end(); |
186 | } |
187 | |
188 | static void show_sig_dialog(pdf_widget *widget) |
189 | { |
190 | fz_try(ctx) |
191 | { |
192 | sig_widget = widget; |
193 | |
194 | if (pdf_signature_is_signed(ctx, pdf, widget->obj)) |
195 | { |
196 | sig_cert_error = pdf_check_certificate(ctx, pdf, widget->obj); |
197 | sig_digest_error = pdf_check_digest(ctx, pdf, widget->obj); |
198 | sig_subsequent_edits = pdf_signature_incremental_change_since_signing(ctx, pdf, widget->obj); |
199 | pdf_signature_designated_name(ctx, pdf, widget->obj, sig_designated_name, sizeof(sig_designated_name)); |
200 | ui.dialog = sig_verify_dialog; |
201 | } |
202 | else |
203 | { |
204 | ui.dialog = sig_sign_dialog; |
205 | } |
206 | } |
207 | fz_catch(ctx) |
208 | ui_show_warning_dialog("%s" , fz_caught_message(ctx)); |
209 | } |
210 | |
211 | static pdf_widget *tx_widget; |
212 | static struct input tx_input; |
213 | |
214 | static void tx_dialog(void) |
215 | { |
216 | int ff = pdf_field_flags(ctx, tx_widget->obj); |
217 | const char *label = pdf_field_label(ctx, tx_widget->obj); |
218 | int tx_h = (ff & PDF_TX_FIELD_IS_MULTILINE) ? 10 : 1; |
219 | int lbl_h = ui_break_lines((char*)label, NULL, 20, 394, NULL); |
220 | int is; |
221 | |
222 | ui_dialog_begin(400, (ui.gridsize+4)*3 + ui.lineheight*(tx_h+lbl_h-2)); |
223 | { |
224 | ui_layout(T, X, NW, 2, 2); |
225 | ui_label("%s" , label); |
226 | is = ui_input(&tx_input, 200, tx_h); |
227 | |
228 | ui_layout(B, X, NW, 2, 2); |
229 | ui_panel_begin(0, ui.gridsize, 0, 0, 0); |
230 | { |
231 | ui_layout(R, NONE, S, 0, 0); |
232 | if (ui_button("Cancel" ) || (!ui.focus && ui.key == KEY_ESCAPE)) |
233 | ui.dialog = NULL; |
234 | ui_spacer(); |
235 | if (ui_button("Okay" ) || is == UI_INPUT_ACCEPT) |
236 | { |
237 | pdf_set_text_field_value(ctx, tx_widget, tx_input.text); |
238 | if (pdf_update_page(ctx, tx_widget->page)) |
239 | render_page(); |
240 | ui.dialog = NULL; |
241 | } |
242 | } |
243 | ui_panel_end(); |
244 | } |
245 | ui_dialog_end(); |
246 | } |
247 | |
248 | void show_tx_dialog(pdf_widget *widget) |
249 | { |
250 | ui_input_init(&tx_input, pdf_field_value(ctx, widget->obj)); |
251 | ui.focus = &tx_input; |
252 | ui.dialog = tx_dialog; |
253 | tx_widget = widget; |
254 | } |
255 | |
256 | static pdf_widget *ch_widget; |
257 | static void ch_dialog(void) |
258 | { |
259 | const char *label; |
260 | const char *value; |
261 | const char **options; |
262 | int n, choice; |
263 | int label_h; |
264 | |
265 | label = pdf_field_label(ctx, ch_widget->obj); |
266 | label_h = ui_break_lines((char*)label, NULL, 20, 394, NULL); |
267 | n = pdf_choice_widget_options(ctx, ch_widget->page->doc, ch_widget, 0, NULL); |
268 | options = fz_malloc_array(ctx, n, const char *); |
269 | pdf_choice_widget_options(ctx, ch_widget->page->doc, ch_widget, 0, options); |
270 | value = pdf_field_value(ctx, ch_widget->obj); |
271 | |
272 | ui_dialog_begin(400, (ui.gridsize+4)*3 + ui.lineheight*(label_h-1)); |
273 | { |
274 | ui_layout(T, X, NW, 2, 2); |
275 | |
276 | ui_label("%s" , label); |
277 | choice = ui_select("Widget/Ch" , value, options, n); |
278 | if (choice >= 0) |
279 | pdf_set_choice_field_value(ctx, ch_widget, options[choice]); |
280 | |
281 | ui_layout(B, X, NW, 2, 2); |
282 | ui_panel_begin(0, ui.gridsize, 0, 0, 0); |
283 | { |
284 | ui_layout(R, NONE, S, 0, 0); |
285 | if (ui_button("Cancel" ) || (!ui.focus && ui.key == KEY_ESCAPE)) |
286 | ui.dialog = NULL; |
287 | ui_spacer(); |
288 | if (ui_button("Okay" )) |
289 | { |
290 | if (pdf_update_page(ctx, ch_widget->page)) |
291 | render_page(); |
292 | ui.dialog = NULL; |
293 | } |
294 | } |
295 | ui_panel_end(); |
296 | } |
297 | ui_dialog_end(); |
298 | |
299 | fz_free(ctx, options); |
300 | } |
301 | |
302 | void do_widget_canvas(fz_irect canvas_area) |
303 | { |
304 | pdf_widget *widget; |
305 | fz_rect bounds; |
306 | fz_irect area; |
307 | |
308 | if (!pdf) |
309 | return; |
310 | |
311 | for (widget = pdf_first_widget(ctx, page); widget; widget = pdf_next_widget(ctx, widget)) |
312 | { |
313 | bounds = pdf_bound_widget(ctx, widget); |
314 | bounds = fz_transform_rect(bounds, view_page_ctm); |
315 | area = fz_irect_from_rect(bounds); |
316 | |
317 | if (ui_mouse_inside(canvas_area) && ui_mouse_inside(area)) |
318 | { |
319 | if (!widget->is_hot) |
320 | pdf_annot_event_enter(ctx, widget); |
321 | widget->is_hot = 1; |
322 | |
323 | ui.hot = widget; |
324 | if (!ui.active && ui.down) |
325 | { |
326 | ui.active = widget; |
327 | pdf_annot_event_down(ctx, widget); |
328 | if (selected_annot != widget) |
329 | { |
330 | if (selected_annot && pdf_annot_type(ctx, selected_annot) == PDF_ANNOT_WIDGET) |
331 | pdf_annot_event_blur(ctx, selected_annot); |
332 | selected_annot = widget; |
333 | pdf_annot_event_focus(ctx, widget); |
334 | } |
335 | } |
336 | } |
337 | else |
338 | { |
339 | if (widget->is_hot) |
340 | pdf_annot_event_exit(ctx, widget); |
341 | widget->is_hot = 0; |
342 | } |
343 | |
344 | /* Set is_hot and is_active to select current appearance */ |
345 | widget->is_active = (ui.active == widget && ui.down); |
346 | |
347 | if (showform) |
348 | { |
349 | glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); |
350 | glEnable(GL_BLEND); |
351 | glColor4f(0, 0, 1, 0.1f); |
352 | glRectf(area.x0, area.y0, area.x1, area.y1); |
353 | glDisable(GL_BLEND); |
354 | } |
355 | |
356 | if (ui.active == widget || (!ui.active && ui.hot == widget)) |
357 | { |
358 | glLineStipple(1, 0xAAAA); |
359 | glEnable(GL_LINE_STIPPLE); |
360 | glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO); |
361 | glEnable(GL_BLEND); |
362 | glColor4f(1, 1, 1, 1); |
363 | glBegin(GL_LINE_LOOP); |
364 | glVertex2f(area.x0-0.5f, area.y0-0.5f); |
365 | glVertex2f(area.x1+0.5f, area.y0-0.5f); |
366 | glVertex2f(area.x1+0.5f, area.y1+0.5f); |
367 | glVertex2f(area.x0-0.5f, area.y1+0.5f); |
368 | glEnd(); |
369 | glDisable(GL_BLEND); |
370 | glDisable(GL_LINE_STIPPLE); |
371 | } |
372 | |
373 | if (ui.hot == widget && ui.active == widget && !ui.down) |
374 | { |
375 | pdf_annot_event_up(ctx, widget); |
376 | |
377 | if (pdf_widget_type(ctx, widget) == PDF_WIDGET_TYPE_SIGNATURE) |
378 | { |
379 | show_sig_dialog(widget); |
380 | } |
381 | else |
382 | { |
383 | if (pdf_field_flags(ctx, widget->obj) & PDF_FIELD_IS_READ_ONLY) |
384 | continue; |
385 | |
386 | switch (pdf_widget_type(ctx, widget)) |
387 | { |
388 | default: |
389 | break; |
390 | case PDF_WIDGET_TYPE_CHECKBOX: |
391 | case PDF_WIDGET_TYPE_RADIOBUTTON: |
392 | pdf_toggle_widget(ctx, widget); |
393 | break; |
394 | case PDF_WIDGET_TYPE_TEXT: |
395 | show_tx_dialog(widget); |
396 | break; |
397 | case PDF_WIDGET_TYPE_COMBOBOX: |
398 | case PDF_WIDGET_TYPE_LISTBOX: |
399 | ui.dialog = ch_dialog; |
400 | ch_widget = widget; |
401 | break; |
402 | } |
403 | } |
404 | |
405 | } |
406 | } |
407 | |
408 | if (pdf_update_page(ctx, page)) |
409 | render_page(); |
410 | } |
411 | |