1 /* gpastreamdecryptop.c - The GpaOperation object.
2 * Copyright (C) 2007, 2008 g10 Code GmbH
3 *
4 * This file is part of GPA.
5 *
6 * GPA is free software; you can redistribute it and/or modify it
7 * 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 * GPA is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 * License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <config.h>
21
22 #include <glib.h>
23
24 #include "gpgmetools.h"
25 #include "recipientdlg.h"
26 #include "gpawidgets.h"
27 #include "gpastreamencryptop.h"
28 #include "selectkeydlg.h"
29
30
31 struct _GpaStreamEncryptOperation
32 {
33 GpaStreamOperation parent;
34
35 SelectKeyDlg *key_dialog;
36 RecipientDlg *recp_dialog;
37 GSList *recipients;
38 gpgme_key_t *keys;
39 gpgme_protocol_t selected_protocol;
40 };
41
42
43 struct _GpaStreamEncryptOperationClass
44 {
45 GpaStreamOperationClass parent_class;
46 };
47
48
49
50 /* Indentifiers for our properties. */
51 enum
52 {
53 PROP_0,
54 PROP_RECIPIENTS,
55 PROP_RECIPIENT_KEYS,
56 PROP_PROTOCOL
57 };
58
59
60
61 static void response_cb (GtkDialog *dialog,
62 gint response,
63 gpointer user_data);
64 static gboolean start_encryption_cb (gpointer data);
65 static void done_error_cb (GpaContext *context, gpg_error_t err,
66 GpaStreamEncryptOperation *op);
67 static void done_cb (GpaContext *context, gpg_error_t err,
68 GpaStreamEncryptOperation *op);
69
70 static GObjectClass *parent_class;
71
72
73
74 /* Helper to be used as a GFunc for free. */
75 static void
free_func(void * p,void * dummy)76 free_func (void *p, void *dummy)
77 {
78 (void)dummy;
79 g_free (p);
80 }
81
82
83 static void
release_recipients(GSList * recipients)84 release_recipients (GSList *recipients)
85 {
86 if (recipients)
87 {
88 g_slist_foreach (recipients, free_func, NULL);
89 g_slist_free (recipients);
90 }
91 }
92
93 /* Return a deep copy of the recipients list. */
94 static GSList *
copy_recipients(GSList * recipients)95 copy_recipients (GSList *recipients)
96 {
97 GSList *recp, *newlist;
98
99 newlist= NULL;
100 for (recp = recipients; recp; recp = g_slist_next (recp))
101 newlist = g_slist_append (newlist, g_strdup (recp->data));
102
103 return newlist;
104 }
105
106
107
108 static void
gpa_stream_encrypt_operation_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)109 gpa_stream_encrypt_operation_get_property (GObject *object, guint prop_id,
110 GValue *value, GParamSpec *pspec)
111 {
112 GpaStreamEncryptOperation *op = GPA_STREAM_ENCRYPT_OPERATION (object);
113
114 switch (prop_id)
115 {
116 case PROP_RECIPIENTS:
117 g_value_set_pointer (value, op->recipients);
118 break;
119 case PROP_RECIPIENT_KEYS:
120 g_value_set_pointer (value, op->keys);
121 break;
122 case PROP_PROTOCOL:
123 g_value_set_int (value, op->selected_protocol);
124 break;
125 default:
126 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
127 break;
128 }
129 }
130
131
132 static void
gpa_stream_encrypt_operation_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)133 gpa_stream_encrypt_operation_set_property (GObject *object, guint prop_id,
134 const GValue *value,
135 GParamSpec *pspec)
136 {
137 GpaStreamEncryptOperation *op = GPA_STREAM_ENCRYPT_OPERATION (object);
138
139 switch (prop_id)
140 {
141 case PROP_RECIPIENTS:
142 op->recipients = (GSList*)g_value_get_pointer (value);
143 break;
144 case PROP_RECIPIENT_KEYS:
145 op->keys = (gpgme_key_t*)g_value_get_pointer (value);
146 break;
147 case PROP_PROTOCOL:
148 op->selected_protocol = g_value_get_int (value);
149 break;
150 default:
151 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
152 break;
153 }
154 }
155
156
157 static void
gpa_stream_encrypt_operation_finalize(GObject * object)158 gpa_stream_encrypt_operation_finalize (GObject *object)
159 {
160 GpaStreamEncryptOperation *op = GPA_STREAM_ENCRYPT_OPERATION (object);
161
162 release_recipients (op->recipients);
163 op->recipients = NULL;
164 gpa_gpgme_release_keyarray (op->keys);
165 op->keys = NULL;
166
167 G_OBJECT_CLASS (parent_class)->finalize (object);
168 }
169
170
171 static void
gpa_stream_encrypt_operation_init(GpaStreamEncryptOperation * op)172 gpa_stream_encrypt_operation_init (GpaStreamEncryptOperation *op)
173 {
174 op->key_dialog = NULL;
175 op->recp_dialog = NULL;
176 op->recipients = NULL;
177 op->keys = NULL;
178 op->selected_protocol = GPGME_PROTOCOL_UNKNOWN;
179 }
180
181
182 static GObject*
gpa_stream_encrypt_operation_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)183 gpa_stream_encrypt_operation_constructor
184 (GType type,
185 guint n_construct_properties,
186 GObjectConstructParam *construct_properties)
187 {
188 GObject *object;
189 GpaStreamEncryptOperation *op;
190
191 object = parent_class->constructor (type,
192 n_construct_properties,
193 construct_properties);
194 op = GPA_STREAM_ENCRYPT_OPERATION (object);
195
196 /* Create the recipient key selection dialog if we don't know the
197 keys yet. */
198 if (!op->keys && (!op->recipients || !g_slist_length (op->recipients)))
199 {
200 /* No recipients - use a generic key selection dialog. */
201 op->key_dialog = select_key_dlg_new (GPA_OPERATION (op)->window);
202 g_signal_connect (G_OBJECT (op->key_dialog), "response",
203 G_CALLBACK (response_cb), op);
204 }
205 else if (!op->keys)
206 {
207 /* Caller gave us some recipients - use the mail address
208 matching key selectiion dialog. */
209 op->recp_dialog = recipient_dlg_new (GPA_OPERATION (op)->window);
210 recipient_dlg_set_recipients (op->recp_dialog,
211 op->recipients,
212 op->selected_protocol);
213 g_signal_connect (G_OBJECT (op->recp_dialog), "response",
214 G_CALLBACK (response_cb), op);
215 }
216 else
217 g_idle_add (start_encryption_cb, op);
218
219
220 /* We connect the done signal to two handles. The error handler is
221 called first. */
222 g_signal_connect (G_OBJECT (GPA_OPERATION (op)->context), "done",
223 G_CALLBACK (done_error_cb), op);
224 g_signal_connect (G_OBJECT (GPA_OPERATION (op)->context), "done",
225 G_CALLBACK (done_cb), op);
226
227 gtk_window_set_title
228 (GTK_WINDOW (GPA_STREAM_OPERATION (op)->progress_dialog),
229 _("Encrypting message ..."));
230
231 if (op->key_dialog)
232 gtk_widget_show_all (GTK_WIDGET (op->key_dialog));
233 if (op->recp_dialog)
234 gtk_widget_show_all (GTK_WIDGET (op->recp_dialog));
235
236 return object;
237 }
238
239
240 static void
gpa_stream_encrypt_operation_class_init(GpaStreamEncryptOperationClass * klass)241 gpa_stream_encrypt_operation_class_init (GpaStreamEncryptOperationClass *klass)
242 {
243 GObjectClass *object_class = G_OBJECT_CLASS (klass);
244
245 parent_class = g_type_class_peek_parent (klass);
246
247 object_class->constructor = gpa_stream_encrypt_operation_constructor;
248 object_class->finalize = gpa_stream_encrypt_operation_finalize;
249 object_class->set_property = gpa_stream_encrypt_operation_set_property;
250 object_class->get_property = gpa_stream_encrypt_operation_get_property;
251
252 g_object_class_install_property
253 (object_class, PROP_RECIPIENTS,
254 g_param_spec_pointer
255 ("recipients", "Recipients",
256 "A list of recipients in rfc-822 mailbox format.",
257 G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY));
258 g_object_class_install_property
259 (object_class, PROP_RECIPIENT_KEYS,
260 g_param_spec_pointer
261 ("recipient-keys", "Recipient-keys",
262 "An array of gpgme_key_t with the selected keys.",
263 G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY));
264 g_object_class_install_property
265 (object_class, PROP_PROTOCOL,
266 g_param_spec_int
267 ("protocol", "Protocol",
268 "The gpgme protocol currently selected.",
269 GPGME_PROTOCOL_OpenPGP, GPGME_PROTOCOL_UNKNOWN, GPGME_PROTOCOL_UNKNOWN,
270 G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY));
271
272 }
273
274
275 GType
gpa_stream_encrypt_operation_get_type(void)276 gpa_stream_encrypt_operation_get_type (void)
277 {
278 static GType stream_encrypt_operation_type = 0;
279
280 if (!stream_encrypt_operation_type)
281 {
282 static const GTypeInfo stream_encrypt_operation_info =
283 {
284 sizeof (GpaStreamEncryptOperationClass),
285 (GBaseInitFunc) NULL,
286 (GBaseFinalizeFunc) NULL,
287 (GClassInitFunc) gpa_stream_encrypt_operation_class_init,
288 NULL, /* class_finalize */
289 NULL, /* class_data */
290 sizeof (GpaStreamEncryptOperation),
291 0, /* n_preallocs */
292 (GInstanceInitFunc) gpa_stream_encrypt_operation_init,
293 };
294
295 stream_encrypt_operation_type = g_type_register_static
296 (GPA_STREAM_OPERATION_TYPE, "GpaStreamEncryptOperation",
297 &stream_encrypt_operation_info, 0);
298 }
299
300 return stream_encrypt_operation_type;
301 }
302
303
304 /* Return true if all keys are matching the protocol. */
305 static int
keys_match_protocol_p(gpgme_key_t * keys,gpgme_protocol_t protocol)306 keys_match_protocol_p (gpgme_key_t *keys, gpgme_protocol_t protocol)
307 {
308 int idx;
309
310 if (!keys)
311 return 1; /* No keys: assume match. */
312 for (idx = 0; keys[idx]; idx++)
313 if (keys[idx]->protocol != protocol)
314 return 0;
315 return 1;
316 }
317
318
319 /*
320 * Fire up the encryption.
321 */
322 static void
start_encryption(GpaStreamEncryptOperation * op)323 start_encryption (GpaStreamEncryptOperation *op)
324 {
325 gpg_error_t err;
326 int prep_only = 0;
327
328 if (!op->keys || !op->keys[0])
329 {
330 err = gpg_error (GPG_ERR_NO_PUBKEY);
331 goto leave;
332 }
333
334 if (op->selected_protocol == GPGME_PROTOCOL_OpenPGP)
335 err = gpa_operation_write_status (GPA_OPERATION (op), "PROTOCOL",
336 "OpenPGP", NULL);
337 else if (op->selected_protocol == GPGME_PROTOCOL_CMS)
338 err = gpa_operation_write_status (GPA_OPERATION (op), "PROTOCOL",
339 "CMS", NULL);
340 else
341 err = gpg_error (GPG_ERR_NO_PUBKEY);
342 if (err)
343 goto leave;
344
345 /* Set the output encoding. */
346 if (GPA_STREAM_OPERATION (op)->input_stream
347 && GPA_STREAM_OPERATION (op)->output_stream)
348 {
349 if (gpgme_data_get_encoding (GPA_STREAM_OPERATION(op)->output_stream))
350 gpgme_data_set_encoding
351 (GPA_STREAM_OPERATION (op)->output_stream,
352 gpgme_data_get_encoding (GPA_STREAM_OPERATION(op)->output_stream));
353 else if (op->selected_protocol == GPGME_PROTOCOL_CMS)
354 gpgme_data_set_encoding (GPA_STREAM_OPERATION (op)->output_stream,
355 GPGME_DATA_ENCODING_BASE64);
356 else
357 gpgme_set_armor (GPA_OPERATION (op)->context->ctx, 1);
358
359 if (!keys_match_protocol_p (op->keys, op->selected_protocol))
360 {
361 g_debug ("the selected keys do not match the protocol");
362 err = gpg_error (GPG_ERR_CONFLICT);
363 goto leave;
364 }
365
366 gpgme_set_protocol (GPA_OPERATION (op)->context->ctx,
367 op->selected_protocol);
368
369 /* We always trust the keys because the recipient selection
370 dialog has already sorted unusable out. */
371 err = gpgme_op_encrypt_start (GPA_OPERATION (op)->context->ctx,
372 op->keys, GPGME_ENCRYPT_ALWAYS_TRUST,
373 GPA_STREAM_OPERATION (op)->input_stream,
374 GPA_STREAM_OPERATION (op)->output_stream);
375 if (err)
376 {
377 gpa_gpgme_warning (err);
378 goto leave;
379 }
380
381 /* Show and update the progress dialog. */
382 gtk_widget_show_all (GPA_STREAM_OPERATION (op)->progress_dialog);
383 gpa_progress_dialog_set_label
384 (GPA_PROGRESS_DIALOG (GPA_STREAM_OPERATION (op)->progress_dialog),
385 _("Message encryption"));
386 }
387 else
388 {
389 /* We are just preparing an encryption. */
390 prep_only = 1;
391 err = 0;
392 }
393
394 leave:
395 if (err || prep_only)
396 g_signal_emit_by_name (GPA_OPERATION (op), "completed", err);
397 }
398
399
400 /* The recipient key selection dialog has returned. */
401 static void
response_cb(GtkDialog * dialog,int response,void * user_data)402 response_cb (GtkDialog *dialog, int response, void *user_data)
403 {
404 GpaStreamEncryptOperation *op = user_data;
405
406 gtk_widget_hide (GTK_WIDGET (dialog));
407
408 if (response != GTK_RESPONSE_OK)
409 {
410 /* The dialog was canceled, so we do nothing and complete the
411 operation. */
412 g_signal_emit_by_name (GPA_OPERATION (op), "completed",
413 gpg_error (GPG_ERR_CANCELED));
414 /* FIXME: We might need to destroy the widget in the KEY_DIALOG case. */
415 return;
416 }
417
418 /* Get the keys. */
419 gpa_gpgme_release_keyarray (op->keys);
420 op->keys = NULL;
421 if (op->key_dialog)
422 op->keys = select_key_dlg_get_keys (op->key_dialog);
423 else if (op->recp_dialog)
424 op->keys = recipient_dlg_get_keys (op->recp_dialog, &op->selected_protocol);
425
426 start_encryption (op);
427 }
428
429
430 /* This is the idle function used to start the encryption if no
431 recipient key selection dialog has been requested. */
432 static gboolean
start_encryption_cb(void * user_data)433 start_encryption_cb (void *user_data)
434 {
435 GpaStreamEncryptOperation *op = user_data;
436
437 start_encryption (op);
438
439 return FALSE; /* Remove this callback from the event loop. */
440 }
441
442
443 /*Show an error message. */
444 static void
done_error_cb(GpaContext * context,gpg_error_t err,GpaStreamEncryptOperation * op)445 done_error_cb (GpaContext *context, gpg_error_t err,
446 GpaStreamEncryptOperation *op)
447 {
448 switch (gpg_err_code (err))
449 {
450 case GPG_ERR_NO_ERROR:
451 case GPG_ERR_CANCELED:
452 /* Ignore these */
453 break;
454 /* case GPG_ERR_BAD_PASSPHRASE: */
455 /* gpa_window_error (_("Wrong passphrase!"), GPA_OPERATION (op)->window); */
456 /* break; */
457 default:
458 gpa_gpgme_warn (err, NULL, GPA_OPERATION (op)->context);
459 break;
460 }
461 }
462
463 /* Operation is ready. Tell the server. */
464 static void
done_cb(GpaContext * context,gpg_error_t err,GpaStreamEncryptOperation * op)465 done_cb (GpaContext *context, gpg_error_t err, GpaStreamEncryptOperation *op)
466 {
467 gtk_widget_hide (GPA_STREAM_OPERATION (op)->progress_dialog);
468
469 g_signal_emit_by_name (GPA_OPERATION (op), "completed", err);
470 }
471
472
473
474
475 /* Public API. */
476
477 /* Start encrypting INPUT_STREAM to OUTPUT_STREAM using WINDOW.
478 RECIPIENTS gives a list of recipients and the function matches them
479 with existing keys and selects appropriate keys. RECP_KEYS is
480 either NULL or an array with gpgme keys which will then immediatley
481 be used and suppresses the recipient key selection dialog.
482
483 If it is not possible to unambigiously select keys and SILENT is
484 not given, a key selection dialog offers the user a way to manually
485 select keys. INPUT_STREAM and OUTPUT_STREAM may be given as NULL
486 in which case the function skips the actual encryption step and
487 just verifies the recipients. */
488 GpaStreamEncryptOperation*
gpa_stream_encrypt_operation_new(GtkWidget * window,gpgme_data_t input_stream,gpgme_data_t output_stream,GSList * recipients,gpgme_key_t * recp_keys,gpgme_protocol_t protocol,int silent)489 gpa_stream_encrypt_operation_new (GtkWidget *window,
490 gpgme_data_t input_stream,
491 gpgme_data_t output_stream,
492 GSList *recipients,
493 gpgme_key_t *recp_keys,
494 gpgme_protocol_t protocol,
495 int silent)
496 {
497 GpaStreamEncryptOperation *op;
498
499 /* Fixme: SILENT is not yet implemented. */
500 g_debug ("recipients %p recp_keys %p", recipients, recp_keys);
501 op = g_object_new (GPA_STREAM_ENCRYPT_OPERATION_TYPE,
502 "window", window,
503 "input_stream", input_stream,
504 "output_stream", output_stream,
505 "recipients", copy_recipients (recipients),
506 "recipient-keys", gpa_gpgme_copy_keyarray (recp_keys),
507 "protocol", (int)protocol,
508 NULL);
509
510 return op;
511 }
512
513
514 /* Return an array of keys for the set of recipients of this object.
515 The function also returns the selected protocol. */
516 gpgme_key_t *
gpa_stream_encrypt_operation_get_keys(GpaStreamEncryptOperation * op,gpgme_protocol_t * r_protocol)517 gpa_stream_encrypt_operation_get_keys (GpaStreamEncryptOperation *op,
518 gpgme_protocol_t *r_protocol)
519 {
520 g_return_val_if_fail (op, NULL);
521
522 if (r_protocol)
523 *r_protocol = op->selected_protocol;
524 return gpa_gpgme_copy_keyarray (op->keys);
525 }
526