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