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
13static pdf_widget *sig_widget;
14static char sig_designated_name[500];
15static enum pdf_signature_error sig_cert_error;
16static enum pdf_signature_error sig_digest_error;
17static int sig_subsequent_edits;
18
19static char cert_filename[PATH_MAX];
20static struct input cert_password;
21
22static 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
46static 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
60static 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
87static int cert_file_filter(const char *fn)
88{
89 return !!strstr(fn, ".pfx");
90}
91
92static 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
107static 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
142static 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
188static 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
211static pdf_widget *tx_widget;
212static struct input tx_input;
213
214static 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
248void 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
256static pdf_widget *ch_widget;
257static 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
302void 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