~ubuntu-branches/ubuntu/karmic/libtinymail/karmic

« back to all changes in this revision

Viewing changes to libtinymail-camel/camel-lite/camel/camel-gpg-context.c

  • Committer: Bazaar Package Importer
  • Author(s): Steve Kowalik
  • Date: 2007-10-12 11:21:12 UTC
  • Revision ID: james.westby@ubuntu.com-20071012112112-fod9fs7yrooxjr7i
Tags: upstream-0.0.2
ImportĀ upstreamĀ versionĀ 0.0.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 
2
/*
 
3
 *  Authors: Jeffrey Stedfast <fejj@ximian.com>
 
4
 *
 
5
 *  Copyright 2002 Ximian, Inc. (www.ximian.com)
 
6
 *
 
7
 *  This program is free software; you can redistribute it and/or modify
 
8
 *  it under the terms of the GNU Lesser General Public License as published by
 
9
 *  the Free Software Foundation; either version 2 of the License, or
 
10
 *  (at your option) any later version.
 
11
 *
 
12
 *  This program is distributed in the hope that it will be useful,
 
13
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
 *  GNU Lesser General Public License for more details.
 
16
 *
 
17
 *  You should have received a copy of the GNU Lesser General Public License
 
18
 *  along with this program; if not, write to the Free Software
 
19
 *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
 
20
 *
 
21
 */
 
22
 
 
23
/* Debug states:
 
24
   gpg:sign     dump canonicalised to-be-signed data to a file
 
25
   gpg:verify   dump canonicalised verification and signature data to file
 
26
   gpg:status   print gpg status-fd output to stdout
 
27
*/
 
28
 
 
29
#ifdef HAVE_CONFIG_H
 
30
#include <config.h>
 
31
#endif
 
32
 
 
33
#include <ctype.h>
 
34
#include <errno.h>
 
35
#include <fcntl.h>
 
36
#include <signal.h>
 
37
#include <stdio.h>
 
38
#include <stdlib.h>
 
39
#include <string.h>
 
40
#include <unistd.h>
 
41
#include <sys/stat.h>
 
42
#include <sys/time.h>
 
43
#include <sys/types.h>
 
44
 
 
45
#ifndef G_OS_WIN32
 
46
#include <sys/ioctl.h>
 
47
#include <sys/poll.h>
 
48
#include <sys/wait.h>
 
49
#include <termios.h>
 
50
#endif
 
51
 
 
52
#include <glib.h>
 
53
#include <glib/gi18n-lib.h>
 
54
#include <glib/gstdio.h>
 
55
 
 
56
#include <libedataserver/e-iconv.h>
 
57
 
 
58
#include "camel-debug.h"
 
59
#include "camel-gpg-context.h"
 
60
#include "camel-mime-filter-canon.h"
 
61
#include "camel-mime-filter-charset.h"
 
62
#include "camel-mime-part.h"
 
63
#include "camel-multipart-encrypted.h"
 
64
#include "camel-multipart-signed.h"
 
65
#include "camel-operation.h"
 
66
#include "camel-stream-filter.h"
 
67
#include "camel-stream-fs.h"
 
68
#include "camel-stream-mem.h"
 
69
 
 
70
#define d(x) 
 
71
 
 
72
#ifdef GPG_LOG
 
73
static int logid;
 
74
#endif
 
75
 
 
76
static CamelCipherContextClass *parent_class = NULL;
 
77
 
 
78
/**
 
79
 * camel_gpg_context_new:
 
80
 * @session: session
 
81
 *
 
82
 * Creates a new gpg cipher context object.
 
83
 *
 
84
 * Returns a new gpg cipher context object.
 
85
 **/
 
86
CamelCipherContext *
 
87
camel_gpg_context_new (CamelSession *session)
 
88
{
 
89
        CamelCipherContext *cipher;
 
90
        CamelGpgContext *ctx;
 
91
        
 
92
        g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
 
93
        
 
94
        ctx = (CamelGpgContext *) camel_object_new (camel_gpg_context_get_type ());
 
95
        
 
96
        cipher = (CamelCipherContext *) ctx;
 
97
        cipher->session = session;
 
98
        camel_object_ref (session);
 
99
        
 
100
        return cipher;
 
101
}
 
102
 
 
103
 
 
104
/**
 
105
 * camel_gpg_context_set_always_trust:
 
106
 * @ctx: gpg context
 
107
 * @always_trust always truct flag
 
108
 *
 
109
 * Sets the @always_trust flag on the gpg context which is used for
 
110
 * encryption.
 
111
 **/
 
112
void
 
113
camel_gpg_context_set_always_trust (CamelGpgContext *ctx, gboolean always_trust)
 
114
{
 
115
        g_return_if_fail (CAMEL_IS_GPG_CONTEXT (ctx));
 
116
        
 
117
        ctx->always_trust = always_trust;
 
118
}
 
119
 
 
120
 
 
121
static const char *
 
122
gpg_hash_to_id (CamelCipherContext *context, CamelCipherHash hash)
 
123
{
 
124
        switch (hash) {
 
125
        case CAMEL_CIPHER_HASH_MD2:
 
126
                return "pgp-md2";
 
127
        case CAMEL_CIPHER_HASH_MD5:
 
128
                return "pgp-md5";
 
129
        case CAMEL_CIPHER_HASH_SHA1:
 
130
        case CAMEL_CIPHER_HASH_DEFAULT:
 
131
                return "pgp-sha1";
 
132
        case CAMEL_CIPHER_HASH_RIPEMD160:
 
133
                return "pgp-ripemd160";
 
134
        case CAMEL_CIPHER_HASH_TIGER192:
 
135
                return "pgp-tiger192";
 
136
        case CAMEL_CIPHER_HASH_HAVAL5160:
 
137
                return "pgp-haval-5-160";
 
138
        }
 
139
        
 
140
        return NULL;
 
141
}
 
142
 
 
143
static CamelCipherHash
 
144
gpg_id_to_hash (CamelCipherContext *context, const char *id)
 
145
{
 
146
        if (id) {
 
147
                if (!strcmp (id, "pgp-md2"))
 
148
                        return CAMEL_CIPHER_HASH_MD2;
 
149
                else if (!strcmp (id, "pgp-md5"))
 
150
                        return CAMEL_CIPHER_HASH_MD5;
 
151
                else if (!strcmp (id, "pgp-sha1"))
 
152
                        return CAMEL_CIPHER_HASH_SHA1;
 
153
                else if (!strcmp (id, "pgp-ripemd160"))
 
154
                        return CAMEL_CIPHER_HASH_RIPEMD160;
 
155
                else if (!strcmp (id, "tiger192"))
 
156
                        return CAMEL_CIPHER_HASH_TIGER192;
 
157
                else if (!strcmp (id, "haval-5-160"))
 
158
                        return CAMEL_CIPHER_HASH_HAVAL5160;
 
159
        }
 
160
        
 
161
        return CAMEL_CIPHER_HASH_DEFAULT;
 
162
}
 
163
 
 
164
 
 
165
enum _GpgCtxMode {
 
166
        GPG_CTX_MODE_SIGN,
 
167
        GPG_CTX_MODE_VERIFY,
 
168
        GPG_CTX_MODE_ENCRYPT,
 
169
        GPG_CTX_MODE_DECRYPT,
 
170
        GPG_CTX_MODE_IMPORT,
 
171
        GPG_CTX_MODE_EXPORT,
 
172
};
 
173
 
 
174
enum _GpgTrustMetric {
 
175
        GPG_TRUST_NONE,
 
176
        GPG_TRUST_NEVER,
 
177
        GPG_TRUST_UNDEFINED,
 
178
        GPG_TRUST_MARGINAL,
 
179
        GPG_TRUST_FULLY,
 
180
        GPG_TRUST_ULTIMATE
 
181
};
 
182
 
 
183
struct _GpgCtx {
 
184
        enum _GpgCtxMode mode;
 
185
        CamelSession *session;
 
186
        GHashTable *userid_hint;
 
187
        pid_t pid;
 
188
        
 
189
        char *userid;
 
190
        char *sigfile;
 
191
        GPtrArray *recipients;
 
192
        CamelCipherHash hash;
 
193
        
 
194
        int stdin_fd;
 
195
        int stdout_fd;
 
196
        int stderr_fd;
 
197
        int status_fd;
 
198
        int passwd_fd;  /* only needed for sign/decrypt */
 
199
        
 
200
        /* status-fd buffer */
 
201
        unsigned char *statusbuf;
 
202
        unsigned char *statusptr;
 
203
        unsigned int statusleft;
 
204
        
 
205
        char *need_id;
 
206
        char *passwd;
 
207
        
 
208
        CamelStream *istream;
 
209
        CamelStream *ostream;
 
210
        
 
211
        GByteArray *diagbuf;
 
212
        CamelStream *diagnostics;
 
213
        
 
214
        int exit_status;
 
215
        
 
216
        unsigned int exited:1;
 
217
        unsigned int complete:1;
 
218
        unsigned int seen_eof1:1;
 
219
        unsigned int seen_eof2:1;
 
220
        unsigned int always_trust:1;
 
221
        unsigned int armor:1;
 
222
        unsigned int need_passwd:1;
 
223
        unsigned int send_passwd:1;
 
224
        
 
225
        unsigned int bad_passwds:2;
 
226
        
 
227
        unsigned int hadsig:1;
 
228
        unsigned int badsig:1;
 
229
        unsigned int errsig:1;
 
230
        unsigned int goodsig:1;
 
231
        unsigned int validsig:1;
 
232
        unsigned int nopubkey:1;
 
233
        unsigned int nodata:1;
 
234
        unsigned int trust:3;
 
235
        
 
236
        unsigned int diagflushed:1;
 
237
        
 
238
        unsigned int utf8:1;
 
239
        
 
240
        unsigned int padding:10;
 
241
};
 
242
 
 
243
static struct _GpgCtx *
 
244
gpg_ctx_new (CamelSession *session)
 
245
{
 
246
        struct _GpgCtx *gpg;
 
247
        const char *charset;
 
248
        CamelStream *stream;
 
249
        
 
250
        gpg = g_new (struct _GpgCtx, 1);
 
251
        gpg->mode = GPG_CTX_MODE_SIGN;
 
252
        gpg->session = session;
 
253
        camel_object_ref (session);
 
254
        gpg->userid_hint = g_hash_table_new (g_str_hash, g_str_equal);
 
255
        gpg->complete = FALSE;
 
256
        gpg->seen_eof1 = TRUE;
 
257
        gpg->seen_eof2 = FALSE;
 
258
        gpg->pid = (pid_t) -1;
 
259
        gpg->exit_status = 0;
 
260
        gpg->exited = FALSE;
 
261
        
 
262
        gpg->userid = NULL;
 
263
        gpg->sigfile = NULL;
 
264
        gpg->recipients = NULL;
 
265
        gpg->hash = CAMEL_CIPHER_HASH_DEFAULT;
 
266
        gpg->always_trust = FALSE;
 
267
        gpg->armor = FALSE;
 
268
        
 
269
        gpg->stdin_fd = -1;
 
270
        gpg->stdout_fd = -1;
 
271
        gpg->stderr_fd = -1;
 
272
        gpg->status_fd = -1;
 
273
        gpg->passwd_fd = -1;
 
274
        
 
275
        gpg->statusbuf = g_malloc (128);
 
276
        gpg->statusptr = gpg->statusbuf;
 
277
        gpg->statusleft = 128;
 
278
        
 
279
        gpg->bad_passwds = 0;
 
280
        gpg->need_passwd = FALSE;
 
281
        gpg->send_passwd = FALSE;
 
282
        gpg->need_id = NULL;
 
283
        gpg->passwd = NULL;
 
284
        
 
285
        gpg->nodata = FALSE;
 
286
        gpg->hadsig = FALSE;
 
287
        gpg->badsig = FALSE;
 
288
        gpg->errsig = FALSE;
 
289
        gpg->goodsig = FALSE;
 
290
        gpg->validsig = FALSE;
 
291
        gpg->nopubkey = FALSE;
 
292
        gpg->trust = GPG_TRUST_NONE;
 
293
        
 
294
        gpg->istream = NULL;
 
295
        gpg->ostream = NULL;
 
296
        
 
297
        stream = camel_stream_mem_new ();
 
298
        gpg->diagbuf = CAMEL_STREAM_MEM (stream)->buffer;
 
299
        gpg->diagflushed = FALSE;
 
300
        
 
301
        if ((charset = e_iconv_locale_charset ()) && g_ascii_strcasecmp (charset, "UTF-8") != 0) {
 
302
                CamelMimeFilterCharset *filter;
 
303
                CamelStreamFilter *fstream;
 
304
                
 
305
                gpg->utf8 = FALSE;
 
306
                
 
307
                if ((filter = camel_mime_filter_charset_new_convert (charset, "UTF-8"))) {
 
308
                        fstream = camel_stream_filter_new_with_stream (stream);
 
309
                        camel_stream_filter_add (fstream, (CamelMimeFilter *) filter);
 
310
                        camel_object_unref (filter);
 
311
                        camel_object_unref (stream);
 
312
                        
 
313
                        stream = (CamelStream *) fstream;
 
314
                }
 
315
        } else {
 
316
                gpg->utf8 = TRUE;
 
317
        }
 
318
        
 
319
        gpg->diagnostics = stream;
 
320
        
 
321
        return gpg;
 
322
}
 
323
 
 
324
static void
 
325
gpg_ctx_set_mode (struct _GpgCtx *gpg, enum _GpgCtxMode mode)
 
326
{
 
327
        gpg->mode = mode;
 
328
        gpg->need_passwd = ((gpg->mode == GPG_CTX_MODE_SIGN) || (gpg->mode == GPG_CTX_MODE_DECRYPT));
 
329
}
 
330
 
 
331
static void
 
332
gpg_ctx_set_hash (struct _GpgCtx *gpg, CamelCipherHash hash)
 
333
{
 
334
        gpg->hash = hash;
 
335
}
 
336
 
 
337
static void
 
338
gpg_ctx_set_always_trust (struct _GpgCtx *gpg, gboolean trust)
 
339
{
 
340
        gpg->always_trust = trust;
 
341
}
 
342
 
 
343
static void
 
344
gpg_ctx_set_userid (struct _GpgCtx *gpg, const char *userid)
 
345
{
 
346
        g_free (gpg->userid);
 
347
        gpg->userid = g_strdup (userid);
 
348
}
 
349
 
 
350
static void
 
351
gpg_ctx_add_recipient (struct _GpgCtx *gpg, const char *keyid)
 
352
{
 
353
        if (gpg->mode != GPG_CTX_MODE_ENCRYPT && gpg->mode != GPG_CTX_MODE_EXPORT)
 
354
                return;
 
355
        
 
356
        if (!gpg->recipients)
 
357
                gpg->recipients = g_ptr_array_new ();
 
358
        
 
359
        g_ptr_array_add (gpg->recipients, g_strdup (keyid));
 
360
}
 
361
 
 
362
static void
 
363
gpg_ctx_set_sigfile (struct _GpgCtx *gpg, const char *sigfile)
 
364
{
 
365
        g_free (gpg->sigfile);
 
366
        gpg->sigfile = g_strdup (sigfile);
 
367
}
 
368
 
 
369
static void
 
370
gpg_ctx_set_armor (struct _GpgCtx *gpg, gboolean armor)
 
371
{
 
372
        gpg->armor = armor;
 
373
}
 
374
 
 
375
static void
 
376
gpg_ctx_set_istream (struct _GpgCtx *gpg, CamelStream *istream)
 
377
{
 
378
        camel_object_ref (istream);
 
379
        if (gpg->istream)
 
380
                camel_object_unref (gpg->istream);
 
381
        gpg->istream = istream;
 
382
}
 
383
 
 
384
static void
 
385
gpg_ctx_set_ostream (struct _GpgCtx *gpg, CamelStream *ostream)
 
386
{
 
387
        camel_object_ref (ostream);
 
388
        if (gpg->ostream)
 
389
                camel_object_unref (gpg->ostream);
 
390
        gpg->ostream = ostream;
 
391
        gpg->seen_eof1 = FALSE;
 
392
}
 
393
 
 
394
static const char *
 
395
gpg_ctx_get_diagnostics (struct _GpgCtx *gpg)
 
396
{
 
397
        if (!gpg->diagflushed) {
 
398
                gpg->diagflushed = TRUE;
 
399
                camel_stream_flush (gpg->diagnostics);
 
400
                if (gpg->diagbuf->len == 0)
 
401
                        return NULL;
 
402
                
 
403
                g_byte_array_append (gpg->diagbuf, (guchar*)"", 1);
 
404
        }
 
405
        
 
406
        return (const char *) gpg->diagbuf->data;
 
407
}
 
408
 
 
409
static void
 
410
userid_hint_free (gpointer key, gpointer value, gpointer user_data)
 
411
{
 
412
        g_free (key);
 
413
        g_free (value);
 
414
}
 
415
 
 
416
static void
 
417
gpg_ctx_free (struct _GpgCtx *gpg)
 
418
{
 
419
        int i;
 
420
 
 
421
        if (gpg == NULL)
 
422
                return;
 
423
 
 
424
        if (gpg->session)
 
425
                camel_object_unref (gpg->session);
 
426
        
 
427
        g_hash_table_foreach (gpg->userid_hint, userid_hint_free, NULL);
 
428
        g_hash_table_destroy (gpg->userid_hint);
 
429
        
 
430
        g_free (gpg->userid);
 
431
        
 
432
        g_free (gpg->sigfile);
 
433
        
 
434
        if (gpg->recipients) {
 
435
                for (i = 0; i < gpg->recipients->len; i++)
 
436
                        g_free (gpg->recipients->pdata[i]);
 
437
        
 
438
                g_ptr_array_free (gpg->recipients, TRUE);
 
439
        }
 
440
        
 
441
        if (gpg->stdin_fd != -1)
 
442
                close (gpg->stdin_fd);
 
443
        if (gpg->stdout_fd != -1)
 
444
                close (gpg->stdout_fd);
 
445
        if (gpg->stderr_fd != -1)
 
446
                close (gpg->stderr_fd);
 
447
        if (gpg->status_fd != -1)
 
448
                close (gpg->status_fd);
 
449
        if (gpg->passwd_fd != -1)
 
450
                close (gpg->passwd_fd);
 
451
        
 
452
        g_free (gpg->statusbuf);
 
453
        
 
454
        g_free (gpg->need_id);
 
455
        
 
456
        if (gpg->passwd) {
 
457
                memset (gpg->passwd, 0, strlen (gpg->passwd));
 
458
                g_free (gpg->passwd);
 
459
        }
 
460
        
 
461
        if (gpg->istream)
 
462
                camel_object_unref (gpg->istream);
 
463
        
 
464
        if (gpg->ostream)
 
465
                camel_object_unref (gpg->ostream);
 
466
        
 
467
        camel_object_unref (gpg->diagnostics);
 
468
        
 
469
        g_free (gpg);
 
470
}
 
471
 
 
472
#ifndef G_OS_WIN32
 
473
 
 
474
static const char *
 
475
gpg_hash_str (CamelCipherHash hash)
 
476
{
 
477
        switch (hash) {
 
478
        case CAMEL_CIPHER_HASH_MD2:
 
479
                return "--digest-algo=MD2";
 
480
        case CAMEL_CIPHER_HASH_MD5:
 
481
                return "--digest-algo=MD5";
 
482
        case CAMEL_CIPHER_HASH_SHA1:
 
483
                return "--digest-algo=SHA1";
 
484
        case CAMEL_CIPHER_HASH_RIPEMD160:
 
485
                return "--digest-algo=RIPEMD160";
 
486
        default:
 
487
                return NULL;
 
488
        }
 
489
}
 
490
 
 
491
static GPtrArray *
 
492
gpg_ctx_get_argv (struct _GpgCtx *gpg, int status_fd, char **sfd, int passwd_fd, char **pfd)
 
493
{
 
494
        const char *hash_str;
 
495
        GPtrArray *argv;
 
496
        char *buf;
 
497
        int i;
 
498
        
 
499
        argv = g_ptr_array_new ();
 
500
        g_ptr_array_add (argv, "gpg");
 
501
        
 
502
        g_ptr_array_add (argv, "--verbose");
 
503
        g_ptr_array_add (argv, "--no-secmem-warning");
 
504
        g_ptr_array_add (argv, "--no-greeting");
 
505
        g_ptr_array_add (argv, "--no-tty");
 
506
        if (passwd_fd == -1) {
 
507
                /* only use batch mode if we don't intend on using the
 
508
                   interactive --command-fd option */
 
509
                g_ptr_array_add (argv, "--batch");
 
510
                g_ptr_array_add (argv, "--yes");
 
511
        }
 
512
        
 
513
        *sfd = buf = g_strdup_printf ("--status-fd=%d", status_fd);
 
514
        g_ptr_array_add (argv, buf);
 
515
        
 
516
        if (passwd_fd != -1) {
 
517
                *pfd = buf = g_strdup_printf ("--command-fd=%d", passwd_fd);
 
518
                g_ptr_array_add (argv, buf);
 
519
        }
 
520
        
 
521
        switch (gpg->mode) {
 
522
        case GPG_CTX_MODE_SIGN:
 
523
                g_ptr_array_add (argv, "--sign");
 
524
                g_ptr_array_add (argv, "--detach");
 
525
                if (gpg->armor)
 
526
                        g_ptr_array_add (argv, "--armor");
 
527
                hash_str = gpg_hash_str (gpg->hash);
 
528
                if (hash_str)
 
529
                        g_ptr_array_add (argv, (char *) hash_str);
 
530
                if (gpg->userid) {
 
531
                        g_ptr_array_add (argv, "-u");
 
532
                        g_ptr_array_add (argv, (char *) gpg->userid);
 
533
                }
 
534
                g_ptr_array_add (argv, "--output");
 
535
                g_ptr_array_add (argv, "-");
 
536
                break;
 
537
        case GPG_CTX_MODE_VERIFY:
 
538
                if (!camel_session_is_online (gpg->session)) {
 
539
                        /* this is a deprecated flag to gpg since 1.0.7 */
 
540
                        /*g_ptr_array_add (argv, "--no-auto-key-retrieve");*/
 
541
                        g_ptr_array_add (argv, "--keyserver-options");
 
542
                        g_ptr_array_add (argv, "no-auto-key-retrieve");
 
543
                }
 
544
                g_ptr_array_add (argv, "--verify");
 
545
                if (gpg->sigfile)
 
546
                        g_ptr_array_add (argv, gpg->sigfile);
 
547
                g_ptr_array_add (argv, "-");
 
548
                break;
 
549
        case GPG_CTX_MODE_ENCRYPT:
 
550
                g_ptr_array_add (argv,  "--encrypt");
 
551
                if (gpg->armor)
 
552
                        g_ptr_array_add (argv, "--armor");
 
553
                if (gpg->always_trust)
 
554
                        g_ptr_array_add (argv, "--always-trust");
 
555
                if (gpg->userid) {
 
556
                        g_ptr_array_add (argv, "-u");
 
557
                        g_ptr_array_add (argv, (char *) gpg->userid);
 
558
                }
 
559
                if (gpg->recipients) {
 
560
                        for (i = 0; i < gpg->recipients->len; i++) {
 
561
                                g_ptr_array_add (argv, "-r");
 
562
                                g_ptr_array_add (argv, gpg->recipients->pdata[i]);
 
563
                        }
 
564
                }
 
565
                g_ptr_array_add (argv, "--output");
 
566
                g_ptr_array_add (argv, "-");
 
567
                break;
 
568
        case GPG_CTX_MODE_DECRYPT:
 
569
                g_ptr_array_add (argv, "--decrypt");
 
570
                g_ptr_array_add (argv, "--output");
 
571
                g_ptr_array_add (argv, "-");
 
572
                break;
 
573
        case GPG_CTX_MODE_IMPORT:
 
574
                g_ptr_array_add (argv, "--import");
 
575
                g_ptr_array_add (argv, "-");
 
576
                break;
 
577
        case GPG_CTX_MODE_EXPORT:
 
578
                if (gpg->armor)
 
579
                        g_ptr_array_add (argv, "--armor");
 
580
                g_ptr_array_add (argv, "--export");
 
581
                for (i = 0; i < gpg->recipients->len; i++)
 
582
                        g_ptr_array_add (argv, gpg->recipients->pdata[i]);
 
583
                break;
 
584
        }
 
585
        
 
586
        g_ptr_array_add (argv, NULL);
 
587
        
 
588
        return argv;
 
589
}
 
590
 
 
591
#endif
 
592
 
 
593
static int
 
594
gpg_ctx_op_start (struct _GpgCtx *gpg)
 
595
{
 
596
#ifndef G_OS_WIN32
 
597
        char *status_fd = NULL, *passwd_fd = NULL;
 
598
        int i, maxfd, errnosave, fds[10];
 
599
        GPtrArray *argv;
 
600
        int flags;
 
601
        
 
602
        for (i = 0; i < 10; i++)
 
603
                fds[i] = -1;
 
604
        
 
605
        maxfd = gpg->need_passwd ? 10 : 8;
 
606
        for (i = 0; i < maxfd; i += 2) {
 
607
                if (pipe (fds + i) == -1)
 
608
                        goto exception;
 
609
        }
 
610
        
 
611
        argv = gpg_ctx_get_argv (gpg, fds[7], &status_fd, fds[8], &passwd_fd);
 
612
        
 
613
        if (!(gpg->pid = fork ())) {
 
614
                /* child process */
 
615
                
 
616
                if ((dup2 (fds[0], STDIN_FILENO) < 0 ) ||
 
617
                    (dup2 (fds[3], STDOUT_FILENO) < 0 ) ||
 
618
                    (dup2 (fds[5], STDERR_FILENO) < 0 )) {
 
619
                        _exit (255);
 
620
                }
 
621
                
 
622
                /* Dissociate from camel's controlling terminal so
 
623
                 * that gpg won't be able to read from it.
 
624
                 */
 
625
                setsid ();
 
626
                
 
627
                maxfd = sysconf (_SC_OPEN_MAX);
 
628
                /* Loop over all fds. */
 
629
                for (i = 3; i < maxfd; i++) {
 
630
                        /* don't close the status-fd or passwd-fd */
 
631
                        if (i != fds[7] && i != fds[8])
 
632
                                fcntl (i, F_SETFD, FD_CLOEXEC);
 
633
                }
 
634
                
 
635
                /* run gpg */
 
636
                execvp ("gpg", (char **) argv->pdata);
 
637
                _exit (255);
 
638
        } else if (gpg->pid < 0) {
 
639
                g_ptr_array_free (argv, TRUE);
 
640
                g_free (status_fd);
 
641
                g_free (passwd_fd);
 
642
                goto exception;
 
643
        }
 
644
        
 
645
        g_ptr_array_free (argv, TRUE);
 
646
        g_free (status_fd);
 
647
        g_free (passwd_fd);
 
648
        
 
649
        /* Parent */
 
650
        close (fds[0]);
 
651
        gpg->stdin_fd = fds[1];
 
652
        gpg->stdout_fd = fds[2];
 
653
        close (fds[3]);
 
654
        gpg->stderr_fd = fds[4];
 
655
        close (fds[5]);
 
656
        gpg->status_fd = fds[6];
 
657
        close (fds[7]);
 
658
        if (gpg->need_passwd) {
 
659
                close (fds[8]);
 
660
                gpg->passwd_fd = fds[9];
 
661
                flags = fcntl (gpg->passwd_fd, F_GETFL);
 
662
                fcntl (gpg->passwd_fd, F_SETFL, flags | O_NONBLOCK);
 
663
        }
 
664
        
 
665
        flags = fcntl (gpg->stdin_fd, F_GETFL);
 
666
        fcntl (gpg->stdin_fd, F_SETFL, flags | O_NONBLOCK);
 
667
        
 
668
        flags = fcntl (gpg->stdout_fd, F_GETFL);
 
669
        fcntl (gpg->stdout_fd, F_SETFL, flags | O_NONBLOCK);
 
670
        
 
671
        flags = fcntl (gpg->stderr_fd, F_GETFL);
 
672
        fcntl (gpg->stderr_fd, F_SETFL, flags | O_NONBLOCK);
 
673
        
 
674
        flags = fcntl (gpg->status_fd, F_GETFL);
 
675
        fcntl (gpg->status_fd, F_SETFL, flags | O_NONBLOCK);
 
676
        
 
677
        return 0;
 
678
        
 
679
 exception:
 
680
        
 
681
        errnosave = errno;
 
682
        
 
683
        for (i = 0; i < 10; i++) {
 
684
                if (fds[i] != -1)
 
685
                        close (fds[i]);
 
686
        }
 
687
        
 
688
        errno = errnosave;
 
689
#else
 
690
        /* FIXME: Port me */
 
691
        g_warning ("%s: Not implemented", __FUNCTION__);
 
692
 
 
693
        errno = EINVAL;
 
694
#endif
 
695
        
 
696
        return -1;
 
697
}
 
698
 
 
699
#ifndef G_OS_WIN32
 
700
 
 
701
static const char *
 
702
next_token (const char *in, char **token)
 
703
{
 
704
        const char *start, *inptr = in;
 
705
        
 
706
        while (*inptr == ' ')
 
707
                inptr++;
 
708
        
 
709
        if (*inptr == '\0' || *inptr == '\n') {
 
710
                if (token)
 
711
                        *token = NULL;
 
712
                return inptr;
 
713
        }
 
714
        
 
715
        start = inptr;
 
716
        while (*inptr && *inptr != ' ' && *inptr != '\n')
 
717
                inptr++;
 
718
        
 
719
        if (token)
 
720
                *token = g_strndup (start, inptr - start);
 
721
        
 
722
        return inptr;
 
723
}
 
724
 
 
725
static int
 
726
gpg_ctx_parse_status (struct _GpgCtx *gpg, CamelException *ex)
 
727
{
 
728
        register unsigned char *inptr;
 
729
        const unsigned char *status;
 
730
        size_t nread, nwritten;
 
731
        int len;
 
732
        
 
733
 parse:
 
734
        
 
735
        inptr = gpg->statusbuf;
 
736
        while (inptr < gpg->statusptr && *inptr != '\n')
 
737
                inptr++;
 
738
        
 
739
        if (*inptr != '\n') {
 
740
                /* we don't have enough data buffered to parse this status line */
 
741
                return 0;
 
742
        }
 
743
        
 
744
        *inptr++ = '\0';
 
745
        status = gpg->statusbuf;
 
746
 
 
747
        if (camel_debug("gpg:status"))
 
748
                printf ("status: %s\n", status);
 
749
        
 
750
        if (strncmp ((char*)status, "[GNUPG:] ", 9) != 0) {
 
751
                camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
 
752
                                      _("Unexpected GnuPG status message encountered:\n\n%s"),
 
753
                                      status);
 
754
                return -1;
 
755
        }
 
756
        
 
757
        status += 9;
 
758
        
 
759
        if (!strncmp ((char*)status, "USERID_HINT ", 12)) {
 
760
                char *hint, *user;
 
761
                
 
762
                status += 12;
 
763
                status = (const unsigned char *) next_token ((char*)status, &hint);
 
764
                if (!hint) {
 
765
                        camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
 
766
                                             _("Failed to parse gpg userid hint."));
 
767
                        return -1;
 
768
                }
 
769
                
 
770
                if (g_hash_table_lookup (gpg->userid_hint, hint)) {
 
771
                        /* we already have this userid hint... */
 
772
                        g_free (hint);
 
773
                        goto recycle;
 
774
                }
 
775
                
 
776
                if (gpg->utf8 || !(user = g_locale_to_utf8 ((gchar*)status, -1, &nread, &nwritten, NULL)))
 
777
                        user = g_strdup ((gchar*)status);
 
778
                
 
779
                g_strstrip (user);
 
780
                
 
781
                g_hash_table_insert (gpg->userid_hint, hint, user);
 
782
        } else if (!strncmp ((char*)status, "NEED_PASSPHRASE ", 16)) {
 
783
                char *userid;
 
784
                
 
785
                status += 16;
 
786
                
 
787
                status = (const unsigned char *) next_token ((char*)status, &userid);
 
788
                if (!userid) {
 
789
                        camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
 
790
                                             _("Failed to parse gpg passphrase request."));
 
791
                        return -1;
 
792
                }
 
793
                
 
794
                g_free (gpg->need_id);
 
795
                gpg->need_id = userid;
 
796
        } else if (!strncmp ((char*)status, "NEED_PASSPHRASE_PIN ", 20)) {
 
797
                char *userid;
 
798
                
 
799
                status += 20;
 
800
                
 
801
                status = (const unsigned char *)next_token ((char*)status, &userid);
 
802
                if (!userid) {
 
803
                        camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
 
804
                                             _("Failed to parse gpg passphrase request."));
 
805
                        return -1;
 
806
                }
 
807
                
 
808
                g_free (gpg->need_id);
 
809
                gpg->need_id = userid;
 
810
        } else if (!strncmp ((char*)status, "GET_HIDDEN ", 11)) {
 
811
                const char *name = NULL;
 
812
                char *prompt, *passwd;
 
813
                guint32 flags;
 
814
                
 
815
                status += 11;
 
816
                
 
817
                if (gpg->need_id && !(name = g_hash_table_lookup (gpg->userid_hint, gpg->need_id)))
 
818
                        name = gpg->need_id;
 
819
                else if (!name)
 
820
                        name = "";
 
821
                
 
822
                if (!strncmp ((char*)status, "passphrase.pin.ask", 18)) {
 
823
                        prompt = g_strdup_printf (_("You need a PIN to unlock the key for your\n"
 
824
                                                    "SmartCard: \"%s\""), name);
 
825
                } else if (!strncmp ((char*)status, "passphrase.enter", 16)) {
 
826
                        prompt = g_strdup_printf (_("You need a passphrase to unlock the key for\n"
 
827
                                                    "user: \"%s\""), name);
 
828
                } else {
 
829
                        next_token ((char*)status, &prompt);
 
830
                        camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
 
831
                                              _("Unexpected request from GnuPG for `%s'"), prompt);
 
832
                        g_free (prompt);
 
833
                        return -1;
 
834
                }
 
835
                
 
836
                flags = CAMEL_SESSION_PASSWORD_SECRET | CAMEL_SESSION_PASSPHRASE;
 
837
                if ((passwd = camel_session_get_password (gpg->session, NULL, NULL, prompt, gpg->need_id, flags, ex))) {
 
838
                        if (!gpg->utf8) {
 
839
                                char *opasswd = passwd;
 
840
                                
 
841
                                if ((passwd = g_locale_to_utf8 (passwd, -1, &nread, &nwritten, NULL))) {
 
842
                                        memset (opasswd, 0, strlen (opasswd));
 
843
                                        g_free (opasswd);
 
844
                                } else {
 
845
                                        passwd = opasswd;
 
846
                                }
 
847
                        }
 
848
                        
 
849
                        gpg->passwd = g_strdup_printf ("%s\n", passwd);
 
850
                        memset (passwd, 0, strlen (passwd));
 
851
                        g_free (passwd);
 
852
                        
 
853
                        gpg->send_passwd = TRUE;
 
854
                } else {
 
855
                        if (!camel_exception_is_set (ex))
 
856
                                camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, "Canceled.");
 
857
                        return -1;
 
858
                }
 
859
                
 
860
                g_free (prompt);
 
861
        } else if (!strncmp ((char*)status, "GOOD_PASSPHRASE", 15)) {
 
862
                gpg->bad_passwds = 0;
 
863
        } else if (!strncmp ((char*)status, "BAD_PASSPHRASE", 14)) {
 
864
                gpg->bad_passwds++;
 
865
                
 
866
                camel_session_forget_password (gpg->session, NULL, NULL, gpg->need_id, ex);
 
867
                
 
868
                if (gpg->bad_passwds == 3) {
 
869
                        camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
 
870
                                             _("Failed to unlock secret key: 3 bad passphrases given."));
 
871
                        return -1;
 
872
                }
 
873
        } else if (!strncmp ((char*)status, "UNEXPECTED ", 11)) {
 
874
                /* this is an error */
 
875
                camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
 
876
                                      _("Unexpected response from GnuPG: %s"),
 
877
                                      status + 11);
 
878
                return -1;
 
879
        } else if (!strncmp ((char*)status, "NODATA", 6)) {
 
880
                /* this is an error */
 
881
                /* But we ignore it anyway, we should get other response codes to say why */
 
882
                gpg->nodata = TRUE;
 
883
        } else {
 
884
                /* check to see if we are complete */
 
885
                switch (gpg->mode) {
 
886
                case GPG_CTX_MODE_SIGN:
 
887
                        if (!strncmp ((char*)status, "SIG_CREATED ", 12)) {
 
888
                                /* FIXME: save this state? */
 
889
                        }
 
890
                        break;
 
891
                case GPG_CTX_MODE_VERIFY:
 
892
                        if (!strncmp ((char*)status, "TRUST_", 6)) {
 
893
                                status += 6;
 
894
                                if (!strncmp ((char*)status, "NEVER", 5)) {
 
895
                                        gpg->trust = GPG_TRUST_NEVER;
 
896
                                } else if (!strncmp ((char*)status, "MARGINAL", 8)) {
 
897
                                        gpg->trust = GPG_TRUST_MARGINAL;
 
898
                                } else if (!strncmp ((char*)status, "FULLY", 5)) {
 
899
                                        gpg->trust = GPG_TRUST_FULLY;
 
900
                                } else if (!strncmp ((char*)status, "ULTIMATE", 8)) {
 
901
                                        gpg->trust = GPG_TRUST_ULTIMATE;
 
902
                                } else if (!strncmp ((char*)status, "UNDEFINED", 9)) {
 
903
                                        gpg->trust = GPG_TRUST_UNDEFINED;
 
904
                                }
 
905
                        } else if (!strncmp ((char*)status, "GOODSIG ", 8)) {
 
906
                                gpg->goodsig = TRUE;
 
907
                                gpg->hadsig = TRUE;
 
908
                        } else if (!strncmp ((char*)status, "VALIDSIG ", 9)) {
 
909
                                gpg->validsig = TRUE;
 
910
                        } else if (!strncmp ((char*)status, "BADSIG ", 7)) {
 
911
                                gpg->badsig = FALSE;
 
912
                                gpg->hadsig = TRUE;
 
913
                        } else if (!strncmp ((char*)status, "ERRSIG ", 7)) {
 
914
                                /* Note: NO_PUBKEY often comes after an ERRSIG */
 
915
                                gpg->errsig = FALSE;
 
916
                                gpg->hadsig = TRUE;
 
917
                        } else if (!strncmp ((char*)status, "NO_PUBKEY ", 10)) {
 
918
                                gpg->nopubkey = TRUE;
 
919
                        }
 
920
                        break;
 
921
                case GPG_CTX_MODE_ENCRYPT:
 
922
                        if (!strncmp ((char*)status, "BEGIN_ENCRYPTION", 16)) {
 
923
                                /* nothing to do... but we know to expect data on stdout soon */
 
924
                        } else if (!strncmp ((char*)status, "END_ENCRYPTION", 14)) {
 
925
                                /* nothing to do, but we know the end is near? */
 
926
                        } else if (!strncmp ((char*)status, "NO_RECP", 7)) {
 
927
                                camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
 
928
                                                     _("Failed to encrypt: No valid recipients specified."));
 
929
                                return -1;
 
930
                        }
 
931
                        break;
 
932
                case GPG_CTX_MODE_DECRYPT:
 
933
                        if (!strncmp ((char*)status, "BEGIN_DECRYPTION", 16)) {
 
934
                                /* nothing to do... but we know to expect data on stdout soon */
 
935
                        } else if (!strncmp ((char*)status, "END_DECRYPTION", 14)) {
 
936
                                /* nothing to do, but we know the end is near? */
 
937
                        }
 
938
                        break;
 
939
                case GPG_CTX_MODE_IMPORT:
 
940
                        /* noop */
 
941
                        break;
 
942
                case GPG_CTX_MODE_EXPORT:
 
943
                        /* noop */
 
944
                        break;
 
945
                }
 
946
        }
 
947
        
 
948
 recycle:
 
949
        
 
950
        /* recycle our statusbuf by moving inptr to the beginning of statusbuf */
 
951
        len = gpg->statusptr - inptr;
 
952
        memmove (gpg->statusbuf, inptr, len);
 
953
        
 
954
        len = inptr - gpg->statusbuf;
 
955
        gpg->statusleft += len;
 
956
        gpg->statusptr -= len;
 
957
        
 
958
        /* if we have more data, try parsing the next line? */
 
959
        if (gpg->statusptr > gpg->statusbuf)
 
960
                goto parse;
 
961
        
 
962
        return 0;
 
963
}
 
964
 
 
965
#endif
 
966
 
 
967
#define status_backup(gpg, start, len) G_STMT_START {                     \
 
968
        if (gpg->statusleft <= len) {                                     \
 
969
                unsigned int slen, soff;                                  \
 
970
                                                                          \
 
971
                slen = soff = gpg->statusptr - gpg->statusbuf;            \
 
972
                slen = slen ? slen : 1;                                   \
 
973
                                                                          \
 
974
                while (slen < soff + len)                                 \
 
975
                        slen <<= 1;                                       \
 
976
                                                                          \
 
977
                gpg->statusbuf = g_realloc (gpg->statusbuf, slen + 1);    \
 
978
                gpg->statusptr = gpg->statusbuf + soff;                   \
 
979
                gpg->statusleft = slen - soff;                            \
 
980
        }                                                                 \
 
981
                                                                          \
 
982
        memcpy (gpg->statusptr, start, len);                              \
 
983
        gpg->statusptr += len;                                            \
 
984
        gpg->statusleft -= len;                                           \
 
985
} G_STMT_END
 
986
 
 
987
#ifndef G_OS_WIN32
 
988
 
 
989
static void
 
990
gpg_ctx_op_cancel (struct _GpgCtx *gpg)
 
991
{
 
992
        pid_t retval;
 
993
        int status;
 
994
        
 
995
        if (gpg->exited)
 
996
                return;
 
997
        
 
998
        kill (gpg->pid, SIGTERM);
 
999
        sleep (1);
 
1000
        retval = waitpid (gpg->pid, &status, WNOHANG);
 
1001
        if (retval == (pid_t) 0) {
 
1002
                /* no more mr nice guy... */
 
1003
                kill (gpg->pid, SIGKILL);
 
1004
                sleep (1);
 
1005
                waitpid (gpg->pid, &status, WNOHANG);
 
1006
        }
 
1007
}
 
1008
 
 
1009
#endif
 
1010
 
 
1011
static int
 
1012
gpg_ctx_op_step (struct _GpgCtx *gpg, CamelException *ex)
 
1013
{
 
1014
#ifndef G_OS_WIN32
 
1015
        struct pollfd polls[6];
 
1016
        int status, i, cancel_fd;
 
1017
 
 
1018
        for (i=0;i<6;i++) {
 
1019
                polls[i].fd = -1;
 
1020
                polls[i].events = 0;
 
1021
        }
 
1022
 
 
1023
        if (!gpg->seen_eof1) {
 
1024
                polls[0].fd = gpg->stdout_fd;
 
1025
                polls[0].events = POLLIN;
 
1026
        }
 
1027
 
 
1028
        if (!gpg->seen_eof2) {
 
1029
                polls[1].fd = gpg->stderr_fd;
 
1030
                polls[1].events = POLLIN;
 
1031
        }
 
1032
 
 
1033
        if (!gpg->complete) {
 
1034
                polls[2].fd = gpg->status_fd;
 
1035
                polls[2].events = POLLIN;
 
1036
        }
 
1037
 
 
1038
        polls[3].fd = gpg->stdin_fd;
 
1039
        polls[3].events = POLLOUT;
 
1040
        polls[4].fd = gpg->passwd_fd;
 
1041
        polls[4].events = POLLOUT;
 
1042
        cancel_fd = camel_operation_cancel_fd(NULL);
 
1043
        polls[5].fd = cancel_fd;
 
1044
        polls[5].events = POLLIN;
 
1045
 
 
1046
        do {
 
1047
                for (i=0;i<6;i++)
 
1048
                        polls[i].revents = 0;
 
1049
                status = poll(polls, 6, 30*1000);
 
1050
        } while (status == -1 && errno == EINTR);
 
1051
 
 
1052
        if (status == 0)
 
1053
                return 0;       /* timed out */
 
1054
        else if (status == -1)
 
1055
                goto exception;
 
1056
 
 
1057
        if ((polls[5].revents & POLLIN) && camel_operation_cancel_check(NULL)) {
 
1058
                camel_exception_set(ex, CAMEL_EXCEPTION_USER_CANCEL, "Canceled.");
 
1059
                gpg_ctx_op_cancel(gpg);
 
1060
                return -1;
 
1061
        }
 
1062
 
 
1063
        /* Test each and every file descriptor to see if it's 'ready',
 
1064
           and if so - do what we can with it and then drop through to
 
1065
           the next file descriptor and so on until we've done what we
 
1066
           can to all of them. If one fails along the way, return
 
1067
           -1. */
 
1068
        
 
1069
        if (polls[2].revents & (POLLIN|POLLHUP)) {
 
1070
                /* read the status message and decide what to do... */
 
1071
                char buffer[4096];
 
1072
                ssize_t nread;
 
1073
                
 
1074
                d(printf ("reading from gpg's status-fd...\n"));
 
1075
                
 
1076
                do {
 
1077
                        nread = read (gpg->status_fd, buffer, sizeof (buffer));
 
1078
                } while (nread == -1 && (errno == EINTR || errno == EAGAIN));
 
1079
                if (nread == -1)
 
1080
                        goto exception;
 
1081
                
 
1082
                if (nread > 0) {
 
1083
                        status_backup (gpg, buffer, nread);
 
1084
                        
 
1085
                        if (gpg_ctx_parse_status (gpg, ex) == -1)
 
1086
                                return -1;
 
1087
                } else {
 
1088
                        gpg->complete = TRUE;
 
1089
                }
 
1090
        }
 
1091
        
 
1092
        if ((polls[0].revents & (POLLIN|POLLHUP)) && gpg->ostream) {
 
1093
                char buffer[4096];
 
1094
                ssize_t nread;
 
1095
                
 
1096
                d(printf ("reading gpg's stdout...\n"));
 
1097
                
 
1098
                do {
 
1099
                        nread = read (gpg->stdout_fd, buffer, sizeof (buffer));
 
1100
                } while (nread == -1 && (errno == EINTR || errno == EAGAIN));
 
1101
                if (nread == -1)
 
1102
                        goto exception;
 
1103
                
 
1104
                if (nread > 0) {
 
1105
                        if (camel_stream_write (gpg->ostream, buffer, (size_t) nread) == -1)
 
1106
                                goto exception;
 
1107
                } else {
 
1108
                        gpg->seen_eof1 = TRUE;
 
1109
                }
 
1110
        }
 
1111
        
 
1112
        if (polls[1].revents & (POLLIN|POLLHUP)) {
 
1113
                char buffer[4096];
 
1114
                ssize_t nread;
 
1115
                
 
1116
                d(printf ("reading gpg's stderr...\n"));
 
1117
                
 
1118
                do {
 
1119
                        nread = read (gpg->stderr_fd, buffer, sizeof (buffer));
 
1120
                } while (nread == -1 && (errno == EINTR || errno == EAGAIN));
 
1121
                if (nread == -1)
 
1122
                        goto exception;
 
1123
                
 
1124
                if (nread > 0) {
 
1125
                        camel_stream_write (gpg->diagnostics, buffer, nread);
 
1126
                } else {
 
1127
                        gpg->seen_eof2 = TRUE;
 
1128
                }
 
1129
        }
 
1130
        
 
1131
        if ((polls[4].revents & (POLLOUT|POLLHUP)) && gpg->need_passwd && gpg->send_passwd) {
 
1132
                ssize_t w, nwritten = 0;
 
1133
                size_t n;
 
1134
                
 
1135
                d(printf ("sending gpg our passphrase...\n"));
 
1136
                
 
1137
                /* send the passphrase to gpg */
 
1138
                n = strlen (gpg->passwd);
 
1139
                do {
 
1140
                        do {
 
1141
                                w = write (gpg->passwd_fd, gpg->passwd + nwritten, n - nwritten);
 
1142
                        } while (w == -1 && (errno == EINTR || errno == EAGAIN));
 
1143
                        
 
1144
                        if (w > 0)
 
1145
                                nwritten += w;
 
1146
                } while (nwritten < n && w != -1);
 
1147
                
 
1148
                /* zero and free our passwd buffer */
 
1149
                memset (gpg->passwd, 0, n);
 
1150
                g_free (gpg->passwd);
 
1151
                gpg->passwd = NULL;
 
1152
                
 
1153
                if (w == -1)
 
1154
                        goto exception;
 
1155
                
 
1156
                gpg->send_passwd = FALSE;
 
1157
        }
 
1158
        
 
1159
        if ((polls[3].revents & (POLLOUT|POLLHUP)) && gpg->istream) {
 
1160
                char buffer[4096];
 
1161
                ssize_t nread;
 
1162
                
 
1163
                d(printf ("writing to gpg's stdin...\n"));
 
1164
                
 
1165
                /* write our stream to gpg's stdin */
 
1166
                nread = camel_stream_read (gpg->istream, buffer, sizeof (buffer));
 
1167
                if (nread > 0) {
 
1168
                        ssize_t w, nwritten = 0;
 
1169
                        
 
1170
                        do {
 
1171
                                do {
 
1172
                                        w = write (gpg->stdin_fd, buffer + nwritten, nread - nwritten);
 
1173
                                } while (w == -1 && (errno == EINTR || errno == EAGAIN));
 
1174
                                
 
1175
                                if (w > 0)
 
1176
                                        nwritten += w;
 
1177
                        } while (nwritten < nread && w != -1);
 
1178
                                                
 
1179
                        if (w == -1)
 
1180
                                goto exception;
 
1181
                        
 
1182
                        d(printf ("wrote %d (out of %d) bytes to gpg's stdin\n", nwritten, nread));
 
1183
                }
 
1184
                
 
1185
                if (camel_stream_eos (gpg->istream)) {
 
1186
                        d(printf ("closing gpg's stdin\n"));
 
1187
                        close (gpg->stdin_fd);
 
1188
                        gpg->stdin_fd = -1;
 
1189
                }
 
1190
        }
 
1191
        
 
1192
        return 0;
 
1193
        
 
1194
 exception:
 
1195
        /* always called on an i/o error */
 
1196
        camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Failed to execute gpg: %s"), g_strerror(errno));
 
1197
        gpg_ctx_op_cancel(gpg);
 
1198
#endif
 
1199
        return -1;
 
1200
}
 
1201
 
 
1202
static gboolean
 
1203
gpg_ctx_op_complete (struct _GpgCtx *gpg)
 
1204
{
 
1205
        return gpg->complete && gpg->seen_eof1 && gpg->seen_eof2;}
 
1206
 
 
1207
 
 
1208
#if 0
 
1209
static gboolean
 
1210
gpg_ctx_op_exited (struct _GpgCtx *gpg)
 
1211
{
 
1212
        pid_t retval;
 
1213
        int status;
 
1214
        
 
1215
        if (gpg->exited)
 
1216
                return TRUE;
 
1217
        
 
1218
        retval = waitpid (gpg->pid, &status, WNOHANG);
 
1219
        if (retval == gpg->pid) {
 
1220
                gpg->exit_status = status;
 
1221
                gpg->exited = TRUE;
 
1222
                return TRUE;
 
1223
        }
 
1224
        
 
1225
        return FALSE;
 
1226
}
 
1227
#endif
 
1228
 
 
1229
static int
 
1230
gpg_ctx_op_wait (struct _GpgCtx *gpg)
 
1231
{
 
1232
#ifndef G_OS_WIN32
 
1233
        sigset_t mask, omask;
 
1234
        pid_t retval;
 
1235
        int status;
 
1236
        
 
1237
        if (!gpg->exited) {
 
1238
                sigemptyset (&mask);
 
1239
                sigaddset (&mask, SIGALRM);
 
1240
                sigprocmask (SIG_BLOCK, &mask, &omask);
 
1241
                alarm (1);
 
1242
                retval = waitpid (gpg->pid, &status, 0);
 
1243
                alarm (0);
 
1244
                sigprocmask (SIG_SETMASK, &omask, NULL);
 
1245
                
 
1246
                if (retval == (pid_t) -1 && errno == EINTR) {
 
1247
                        /* The child is hanging: send a friendly reminder. */
 
1248
                        kill (gpg->pid, SIGTERM);
 
1249
                        sleep (1);
 
1250
                        retval = waitpid (gpg->pid, &status, WNOHANG);
 
1251
                        if (retval == (pid_t) 0) {
 
1252
                                /* Still hanging; use brute force. */
 
1253
                                kill (gpg->pid, SIGKILL);
 
1254
                                sleep (1);
 
1255
                                retval = waitpid (gpg->pid, &status, WNOHANG);
 
1256
                        }
 
1257
                }
 
1258
        } else {
 
1259
                status = gpg->exit_status;
 
1260
                retval = gpg->pid;
 
1261
        }
 
1262
        
 
1263
        if (retval != (pid_t) -1 && WIFEXITED (status))
 
1264
                return WEXITSTATUS (status);
 
1265
        else
 
1266
                return -1;
 
1267
#else
 
1268
        return -1;
 
1269
#endif
 
1270
}
 
1271
 
 
1272
 
 
1273
 
 
1274
static int
 
1275
gpg_sign (CamelCipherContext *context, const char *userid, CamelCipherHash hash, CamelMimePart *ipart, CamelMimePart *opart, CamelException *ex)
 
1276
{
 
1277
        struct _GpgCtx *gpg = NULL;
 
1278
        CamelStream *ostream = camel_stream_mem_new(), *istream;
 
1279
        CamelDataWrapper *dw;
 
1280
        CamelContentType *ct;
 
1281
        int res = -1;
 
1282
        CamelMimePart *sigpart;
 
1283
        CamelMultipartSigned *mps;
 
1284
        
 
1285
        /* Note: see rfc2015 or rfc3156, section 5 */
 
1286
        
 
1287
        /* FIXME: stream this, we stream output at least */
 
1288
        istream = camel_stream_mem_new();
 
1289
        if (camel_cipher_canonical_to_stream(ipart, CAMEL_MIME_FILTER_CANON_STRIP|CAMEL_MIME_FILTER_CANON_CRLF|CAMEL_MIME_FILTER_CANON_FROM,
 
1290
                                             istream) == -1) {
 
1291
                camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
 
1292
                                     _("Could not generate signing data: %s"), g_strerror(errno));
 
1293
                goto fail;
 
1294
        }
 
1295
 
 
1296
#ifdef GPG_LOG
 
1297
        if (camel_debug_start("gpg:sign")) {
 
1298
                char *name;
 
1299
                CamelStream *out;
 
1300
 
 
1301
                name = g_strdup_printf("camel-gpg.%d.sign-data", logid++);
 
1302
                out = camel_stream_fs_new_with_name(name, O_CREAT|O_TRUNC|O_WRONLY, 0666);
 
1303
                if (out) {
 
1304
                        printf("Writing gpg signing data to '%s'\n", name);
 
1305
                        camel_stream_write_to_stream(istream, out);
 
1306
                        camel_stream_reset(istream);
 
1307
                        camel_object_unref(out);
 
1308
                }
 
1309
                g_free(name);
 
1310
                camel_debug_end();
 
1311
        }
 
1312
#endif
 
1313
 
 
1314
        gpg = gpg_ctx_new (context->session);
 
1315
        gpg_ctx_set_mode (gpg, GPG_CTX_MODE_SIGN);
 
1316
        gpg_ctx_set_hash (gpg, hash);
 
1317
        gpg_ctx_set_armor (gpg, TRUE);
 
1318
        gpg_ctx_set_userid (gpg, userid);
 
1319
        gpg_ctx_set_istream (gpg, istream);
 
1320
        gpg_ctx_set_ostream (gpg, ostream);
 
1321
        
 
1322
        if (gpg_ctx_op_start (gpg) == -1) {
 
1323
                camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
 
1324
                                      _("Failed to execute gpg: %s"), g_strerror (errno));
 
1325
                goto fail;
 
1326
        }
 
1327
        
 
1328
        while (!gpg_ctx_op_complete (gpg)) {
 
1329
                if (gpg_ctx_op_step (gpg, ex) == -1)
 
1330
                        goto fail;
 
1331
        }
 
1332
        
 
1333
        if (gpg_ctx_op_wait (gpg) != 0) {
 
1334
                const char *diagnostics;
 
1335
                
 
1336
                diagnostics = gpg_ctx_get_diagnostics (gpg);
 
1337
                camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
 
1338
                                     diagnostics && *diagnostics ? diagnostics :
 
1339
                                     _("Failed to execute gpg."));
 
1340
                goto fail;
 
1341
        }
 
1342
 
 
1343
        res = 0;
 
1344
 
 
1345
        dw = camel_data_wrapper_new();
 
1346
        camel_stream_reset(ostream);
 
1347
        camel_data_wrapper_construct_from_stream(dw, ostream);
 
1348
 
 
1349
        sigpart = camel_mime_part_new();
 
1350
        ct = camel_content_type_new("application", "pgp-signature");
 
1351
        camel_content_type_set_param(ct, "name", "signature.asc");
 
1352
        camel_data_wrapper_set_mime_type_field(dw, ct);
 
1353
        camel_content_type_unref(ct);
 
1354
 
 
1355
        camel_medium_set_content_object((CamelMedium *)sigpart, dw);
 
1356
        camel_object_unref(dw);
 
1357
 
 
1358
        camel_mime_part_set_description(sigpart, _("This is a digitally signed message part"));
 
1359
 
 
1360
        mps = camel_multipart_signed_new();
 
1361
        ct = camel_content_type_new("multipart", "signed");
 
1362
        camel_content_type_set_param(ct, "micalg", camel_cipher_hash_to_id(context, hash));
 
1363
        camel_content_type_set_param(ct, "protocol", context->sign_protocol);
 
1364
        camel_data_wrapper_set_mime_type_field((CamelDataWrapper *)mps, ct);
 
1365
        camel_content_type_unref(ct);
 
1366
        camel_multipart_set_boundary((CamelMultipart *)mps, NULL);
 
1367
 
 
1368
        mps->signature = sigpart;
 
1369
        mps->contentraw = istream;
 
1370
        camel_stream_reset(istream);
 
1371
        camel_object_ref(istream);
 
1372
 
 
1373
        camel_medium_set_content_object((CamelMedium *)opart, (CamelDataWrapper *)mps);
 
1374
fail:
 
1375
        camel_object_unref(ostream);
 
1376
        
 
1377
        if (gpg)
 
1378
                gpg_ctx_free (gpg);
 
1379
        
 
1380
        return res;
 
1381
}
 
1382
 
 
1383
 
 
1384
static char *
 
1385
swrite (CamelMimePart *sigpart)
 
1386
{
 
1387
        CamelStream *ostream;
 
1388
        char *template;
 
1389
        int fd, ret;
 
1390
        
 
1391
        template = g_build_filename (g_get_tmp_dir (), "evolution-pgp.XXXXXX", NULL);
 
1392
        if ((fd = g_mkstemp (template)) == -1) {
 
1393
                g_free (template);
 
1394
                return NULL;
 
1395
        }
 
1396
        
 
1397
        /* TODO: This should probably just write the decoded message content out, not the part + headers */
 
1398
        
 
1399
        ostream = camel_stream_fs_new_with_fd (fd);
 
1400
        ret = camel_data_wrapper_write_to_stream((CamelDataWrapper *)sigpart, ostream);
 
1401
        if (ret != -1) {
 
1402
                ret = camel_stream_flush (ostream);
 
1403
                if (ret != -1)
 
1404
                        ret = camel_stream_close (ostream);
 
1405
        }
 
1406
        
 
1407
        camel_object_unref(ostream);
 
1408
        
 
1409
        if (ret == -1) {
 
1410
                g_unlink (template);
 
1411
                g_free (template);
 
1412
                return NULL;
 
1413
        }
 
1414
        
 
1415
        return template;
 
1416
}
 
1417
 
 
1418
static CamelCipherValidity *
 
1419
gpg_verify (CamelCipherContext *context, CamelMimePart *ipart, CamelException *ex)
 
1420
{
 
1421
        CamelCipherValidity *validity;
 
1422
        const char *diagnostics = NULL;
 
1423
        struct _GpgCtx *gpg = NULL;
 
1424
        char *sigfile = NULL;
 
1425
        CamelContentType *ct;
 
1426
        CamelMimePart *sigpart;
 
1427
        CamelStream *istream = NULL;
 
1428
        CamelMultipart *mps;
 
1429
 
 
1430
        mps = (CamelMultipart *)camel_medium_get_content_object((CamelMedium *)ipart);
 
1431
        ct = ((CamelDataWrapper *)mps)->mime_type;
 
1432
        
 
1433
        /* Inline signature (using our fake mime type) or PGP/Mime signature */
 
1434
        if (camel_content_type_is(ct, "multipart", "signed")) {
 
1435
                /* PGP/Mime Signature */
 
1436
                const char *tmp;
 
1437
 
 
1438
                tmp = camel_content_type_param(ct, "protocol");
 
1439
                if (!CAMEL_IS_MULTIPART_SIGNED(mps)
 
1440
                    || tmp == NULL  
 
1441
                    || g_ascii_strcasecmp(tmp, context->sign_protocol) != 0) {
 
1442
                        camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
 
1443
                                      _("Cannot verify message signature: Incorrect message format"));
 
1444
                        return NULL;
 
1445
                }
 
1446
        
 
1447
                if (!(istream = camel_multipart_signed_get_content_stream ((CamelMultipartSigned *) mps, NULL))) {
 
1448
                        camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
 
1449
                                      _("Cannot verify message signature: Incorrect message format"));
 
1450
                        return NULL;
 
1451
                }
 
1452
        
 
1453
                if (!(sigpart = camel_multipart_get_part (mps, CAMEL_MULTIPART_SIGNED_SIGNATURE))) {
 
1454
                        camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
 
1455
                                      _("Cannot verify message signature: Incorrect message format"));
 
1456
                        camel_object_unref (istream);
 
1457
                        return NULL;
 
1458
                }
 
1459
 
 
1460
        } else if (camel_content_type_is(ct, "application", "x-inlinepgp-signed")) {
 
1461
                /* Inline Signed */
 
1462
                CamelDataWrapper *content;
 
1463
                content = camel_medium_get_content_object ((CamelMedium *) ipart);
 
1464
                istream = camel_stream_mem_new();
 
1465
                camel_data_wrapper_decode_to_stream (content, istream);
 
1466
                camel_stream_reset(istream);
 
1467
                sigpart = NULL;
 
1468
        } else {
 
1469
                /* Invalid Mimetype */
 
1470
                camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
 
1471
                              _("Cannot verify message signature: Incorrect message format"));
 
1472
                return NULL;
 
1473
        }
 
1474
        
 
1475
        /* Now start the real work of verifying the message */
 
1476
#ifdef GPG_LOG
 
1477
        if (camel_debug_start("gpg:sign")) {
 
1478
                char *name;
 
1479
                CamelStream *out;
 
1480
 
 
1481
                name = g_strdup_printf("camel-gpg.%d.verify.data", logid);
 
1482
                out = camel_stream_fs_new_with_name(name, O_CREAT|O_TRUNC|O_WRONLY, 0666);
 
1483
                if (out) {
 
1484
                        printf("Writing gpg verify data to '%s'\n", name);
 
1485
                        camel_stream_write_to_stream(istream, out);
 
1486
                        camel_stream_reset(istream);
 
1487
                        camel_object_unref(out);
 
1488
                }
 
1489
                g_free(name);
 
1490
                if (sigpart) {
 
1491
                        name = g_strdup_printf("camel-gpg.%d.verify.signature", logid++);
 
1492
                        out = camel_stream_fs_new_with_name(name, O_CREAT|O_TRUNC|O_WRONLY, 0666);
 
1493
                        if (out) {
 
1494
                                printf("Writing gpg verify signature to '%s'\n", name);
 
1495
                                camel_data_wrapper_write_to_stream((CamelDataWrapper *)sigpart, out);
 
1496
                                camel_object_unref(out);
 
1497
                        }
 
1498
                        g_free(name);
 
1499
                }
 
1500
                camel_debug_end();
 
1501
        }
 
1502
#endif
 
1503
 
 
1504
        if (sigpart) {
 
1505
                sigfile = swrite (sigpart);
 
1506
                if (sigfile == NULL) {
 
1507
                        camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
 
1508
                                      _("Cannot verify message signature: could not create temp file: %s"),
 
1509
                                      g_strerror (errno));
 
1510
                        goto exception;
 
1511
                }
 
1512
        }
 
1513
        
 
1514
        camel_stream_reset(istream);
 
1515
        gpg = gpg_ctx_new (context->session);
 
1516
        gpg_ctx_set_mode (gpg, GPG_CTX_MODE_VERIFY);
 
1517
        if (sigfile)
 
1518
                gpg_ctx_set_sigfile (gpg, sigfile);
 
1519
        gpg_ctx_set_istream (gpg, istream);
 
1520
        
 
1521
        if (gpg_ctx_op_start (gpg) == -1) {
 
1522
                camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
 
1523
                                     _("Failed to execute gpg."));
 
1524
                goto exception;
 
1525
        }
 
1526
        
 
1527
        while (!gpg_ctx_op_complete (gpg)) {
 
1528
                if (gpg_ctx_op_step (gpg, ex) == -1)
 
1529
                        goto exception;
 
1530
        }
 
1531
        
 
1532
        gpg_ctx_op_wait (gpg);
 
1533
        validity = camel_cipher_validity_new ();
 
1534
        diagnostics = gpg_ctx_get_diagnostics (gpg);
 
1535
        camel_cipher_validity_set_description (validity, diagnostics);
 
1536
        if (gpg->validsig) {
 
1537
                if (gpg->trust == GPG_TRUST_UNDEFINED || gpg->trust == GPG_TRUST_NONE)
 
1538
                        validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_UNKNOWN;
 
1539
                else if (gpg->trust != GPG_TRUST_NEVER)
 
1540
                        validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_GOOD;
 
1541
                else
 
1542
                        validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
 
1543
        } else {
 
1544
                validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
 
1545
        }
 
1546
        
 
1547
        gpg_ctx_free (gpg);
 
1548
        
 
1549
        if (sigfile) {
 
1550
                g_unlink (sigfile);
 
1551
                g_free (sigfile);
 
1552
        }
 
1553
        camel_object_unref(istream);
 
1554
        
 
1555
        return validity;
 
1556
        
 
1557
 exception:
 
1558
        
 
1559
        if (gpg != NULL)
 
1560
                gpg_ctx_free (gpg);
 
1561
        
 
1562
        if (istream)
 
1563
                camel_object_unref(istream);
 
1564
 
 
1565
        if (sigfile) {
 
1566
                g_unlink (sigfile);
 
1567
                g_free (sigfile);
 
1568
        }
 
1569
        
 
1570
        return NULL;
 
1571
}
 
1572
 
 
1573
static int
 
1574
gpg_encrypt (CamelCipherContext *context, const char *userid, GPtrArray *recipients, struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex)
 
1575
{
 
1576
        CamelGpgContext *ctx = (CamelGpgContext *) context;
 
1577
        struct _GpgCtx *gpg;
 
1578
        int i, res = -1;
 
1579
        CamelStream *istream, *ostream, *vstream;
 
1580
        CamelMimePart *encpart, *verpart;
 
1581
        CamelDataWrapper *dw;
 
1582
        CamelContentType *ct;
 
1583
        CamelMultipartEncrypted *mpe;
 
1584
 
 
1585
        ostream = camel_stream_mem_new();
 
1586
        istream = camel_stream_mem_new();
 
1587
        if (camel_cipher_canonical_to_stream(ipart, CAMEL_MIME_FILTER_CANON_CRLF, istream) == -1) {
 
1588
                camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
 
1589
                                     _("Could not generate encrypting data: %s"), g_strerror(errno));
 
1590
                goto fail1;
 
1591
        }
 
1592
 
 
1593
        gpg = gpg_ctx_new (context->session);
 
1594
        gpg_ctx_set_mode (gpg, GPG_CTX_MODE_ENCRYPT);
 
1595
        gpg_ctx_set_armor (gpg, TRUE);
 
1596
        gpg_ctx_set_userid (gpg, userid);
 
1597
        gpg_ctx_set_istream (gpg, istream);
 
1598
        gpg_ctx_set_ostream (gpg, ostream);
 
1599
        gpg_ctx_set_always_trust (gpg, ctx->always_trust);
 
1600
 
 
1601
        for (i = 0; i < recipients->len; i++) {
 
1602
                gpg_ctx_add_recipient (gpg, recipients->pdata[i]);
 
1603
        }
 
1604
        
 
1605
        if (gpg_ctx_op_start (gpg) == -1) {
 
1606
                camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Failed to execute gpg."));
 
1607
                goto fail;
 
1608
        }
 
1609
 
 
1610
        /* FIXME: move tihs to a common routine */
 
1611
        while (!gpg_ctx_op_complete(gpg)) {
 
1612
                if (gpg_ctx_op_step (gpg, ex) == -1)
 
1613
                        goto fail;
 
1614
        }
 
1615
        
 
1616
        if (gpg_ctx_op_wait (gpg) != 0) {
 
1617
                const char *diagnostics;
 
1618
                
 
1619
                diagnostics = gpg_ctx_get_diagnostics (gpg);
 
1620
                camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
 
1621
                                     diagnostics && *diagnostics ? diagnostics : _("Failed to execute gpg."));
 
1622
                goto fail;
 
1623
        }
 
1624
 
 
1625
        res = 0;
 
1626
 
 
1627
        dw = camel_data_wrapper_new();
 
1628
        camel_data_wrapper_construct_from_stream(dw, ostream);
 
1629
 
 
1630
        encpart = camel_mime_part_new();
 
1631
        ct = camel_content_type_new("application", "octet-stream");
 
1632
        camel_content_type_set_param(ct, "name", "encrypted.asc");
 
1633
        camel_data_wrapper_set_mime_type_field(dw, ct);
 
1634
        camel_content_type_unref(ct);
 
1635
 
 
1636
        camel_medium_set_content_object((CamelMedium *)encpart, dw);
 
1637
        camel_object_unref(dw);
 
1638
 
 
1639
        camel_mime_part_set_description(encpart, _("This is a digitally encrypted message part"));
 
1640
 
 
1641
        vstream = camel_stream_mem_new();
 
1642
        camel_stream_write(vstream, "Version: 1\n", strlen("Version: 1\n"));
 
1643
        camel_stream_reset(vstream);
 
1644
 
 
1645
        verpart = camel_mime_part_new();
 
1646
        dw = camel_data_wrapper_new();
 
1647
        camel_data_wrapper_set_mime_type(dw, context->encrypt_protocol);
 
1648
        camel_data_wrapper_construct_from_stream(dw, vstream);
 
1649
        camel_object_unref(vstream);
 
1650
        camel_medium_set_content_object((CamelMedium *)verpart, dw);
 
1651
        camel_object_unref(dw);
 
1652
 
 
1653
        mpe = camel_multipart_encrypted_new();
 
1654
        ct = camel_content_type_new("multipart", "encrypted");
 
1655
        camel_content_type_set_param(ct, "protocol", context->encrypt_protocol);
 
1656
        camel_data_wrapper_set_mime_type_field((CamelDataWrapper *)mpe, ct);
 
1657
        camel_content_type_unref(ct);
 
1658
        camel_multipart_set_boundary((CamelMultipart *)mpe, NULL);
 
1659
 
 
1660
        mpe->decrypted = ipart;
 
1661
        camel_object_ref(ipart);
 
1662
 
 
1663
        camel_multipart_add_part((CamelMultipart *)mpe, verpart);
 
1664
        camel_object_unref(verpart);
 
1665
        camel_multipart_add_part((CamelMultipart *)mpe, encpart);
 
1666
        camel_object_unref(encpart);
 
1667
 
 
1668
        camel_medium_set_content_object((CamelMedium *)opart, (CamelDataWrapper *)mpe);
 
1669
fail:
 
1670
        gpg_ctx_free(gpg);
 
1671
fail1:
 
1672
        camel_object_unref(istream);
 
1673
        camel_object_unref(ostream);
 
1674
                
 
1675
        return res;
 
1676
}
 
1677
 
 
1678
static CamelCipherValidity *
 
1679
gpg_decrypt(CamelCipherContext *context, CamelMimePart *ipart, CamelMimePart *opart, CamelException *ex)
 
1680
{
 
1681
        struct _GpgCtx *gpg;
 
1682
        CamelCipherValidity *valid = NULL;
 
1683
        CamelStream *ostream, *istream;
 
1684
        CamelDataWrapper *content;
 
1685
        CamelMimePart *encrypted;
 
1686
        CamelMultipart *mp;
 
1687
        CamelContentType *ct;
 
1688
        int rv;
 
1689
        
 
1690
        content = camel_medium_get_content_object((CamelMedium *)ipart);
 
1691
        ct = camel_mime_part_get_content_type((CamelMimePart *)content);
 
1692
        /* Encrypted part (using our fake mime type) or PGP/Mime multipart */
 
1693
        if (camel_content_type_is(ct, "multipart", "encrypted")) {      
 
1694
                mp = (CamelMultipart *) camel_medium_get_content_object ((CamelMedium *) ipart);
 
1695
                if (!(encrypted = camel_multipart_get_part (mp, CAMEL_MULTIPART_ENCRYPTED_CONTENT))) {
 
1696
                        camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Failed to decrypt MIME part: protocol error"));
 
1697
                        return NULL;
 
1698
                }
 
1699
 
 
1700
                content = camel_medium_get_content_object ((CamelMedium *) encrypted);
 
1701
        } else if (camel_content_type_is(ct, "application", "x-inlinepgp-encrypted")) {
 
1702
                content = camel_medium_get_content_object ((CamelMedium *) ipart);
 
1703
 
 
1704
        } else {
 
1705
                /* Invalid Mimetype */
 
1706
                camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
 
1707
                                _("Cannot decrypt message: Incorrect message format"));
 
1708
                return NULL;
 
1709
        }
 
1710
        
 
1711
        istream = camel_stream_mem_new();
 
1712
        camel_data_wrapper_decode_to_stream (content, istream);
 
1713
        camel_stream_reset(istream);
 
1714
        
 
1715
        ostream = camel_stream_mem_new();
 
1716
        camel_stream_mem_set_secure((CamelStreamMem *)ostream);
 
1717
 
 
1718
        gpg = gpg_ctx_new (context->session);
 
1719
        gpg_ctx_set_mode (gpg, GPG_CTX_MODE_DECRYPT);
 
1720
        gpg_ctx_set_istream (gpg, istream);
 
1721
        gpg_ctx_set_ostream (gpg, ostream);
 
1722
        
 
1723
        if (gpg_ctx_op_start (gpg) == -1) {
 
1724
                camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
 
1725
                                     _("Failed to execute gpg."));
 
1726
                goto fail;
 
1727
        }
 
1728
        
 
1729
        while (!gpg_ctx_op_complete (gpg)) {
 
1730
                if (gpg_ctx_op_step (gpg, ex) == -1)
 
1731
                        goto fail;
 
1732
        }
 
1733
        
 
1734
        if (gpg_ctx_op_wait (gpg) != 0) {
 
1735
                const char *diagnostics;
 
1736
                
 
1737
                diagnostics = gpg_ctx_get_diagnostics (gpg);
 
1738
                camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
 
1739
                                     diagnostics && *diagnostics ? diagnostics :
 
1740
                                     _("Failed to execute gpg."));
 
1741
                goto fail;
 
1742
        }
 
1743
 
 
1744
        camel_stream_reset(ostream);
 
1745
        if (camel_content_type_is(ct, "multipart", "encrypted")) {
 
1746
                /* Multipart encrypted - parse a full mime part */
 
1747
                rv = camel_data_wrapper_construct_from_stream((CamelDataWrapper *)opart, ostream);
 
1748
        } else {
 
1749
                /* Inline signed - raw data (may not be a mime part) */
 
1750
                CamelDataWrapper *dw;
 
1751
                dw = camel_data_wrapper_new ();
 
1752
                rv = camel_data_wrapper_construct_from_stream(dw, ostream);
 
1753
                camel_data_wrapper_set_mime_type(dw, "application/octet-stream");
 
1754
                camel_medium_set_content_object((CamelMedium *)opart, dw);
 
1755
                camel_object_unref(dw);
 
1756
                /* Set mime/type of this new part to application/octet-stream to force type snooping */
 
1757
                camel_mime_part_set_content_type(opart, "application/octet-stream");
 
1758
        }
 
1759
        if (rv != -1) { 
 
1760
                valid = camel_cipher_validity_new();
 
1761
                valid->encrypt.description = g_strdup(_("Encrypted content"));
 
1762
                valid->encrypt.status = CAMEL_CIPHER_VALIDITY_ENCRYPT_ENCRYPTED;
 
1763
                
 
1764
                if (gpg->hadsig) {
 
1765
                        if (gpg->validsig) {
 
1766
                                if (gpg->trust == GPG_TRUST_UNDEFINED || gpg->trust == GPG_TRUST_NONE)
 
1767
                                        valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_UNKNOWN;
 
1768
                                else if (gpg->trust != GPG_TRUST_NEVER)
 
1769
                                        valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_GOOD;
 
1770
                                else
 
1771
                                        valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
 
1772
                        } else if (gpg->nopubkey) {
 
1773
                                valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_UNKNOWN;
 
1774
                        } else {
 
1775
                                valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
 
1776
                        }
 
1777
                }
 
1778
        } else {
 
1779
                camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
 
1780
                                     _("Unable to parse message content"));
 
1781
        }
 
1782
        
 
1783
 fail:
 
1784
        camel_object_unref(ostream);
 
1785
        camel_object_unref(istream);
 
1786
        gpg_ctx_free (gpg);
 
1787
 
 
1788
        return valid;
 
1789
}
 
1790
 
 
1791
static int
 
1792
gpg_import_keys (CamelCipherContext *context, CamelStream *istream, CamelException *ex)
 
1793
{
 
1794
        struct _GpgCtx *gpg;
 
1795
        int res = -1;
 
1796
 
 
1797
        gpg = gpg_ctx_new (context->session);
 
1798
        gpg_ctx_set_mode (gpg, GPG_CTX_MODE_IMPORT);
 
1799
        gpg_ctx_set_istream (gpg, istream);
 
1800
        
 
1801
        if (gpg_ctx_op_start (gpg) == -1) {
 
1802
                camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
 
1803
                                      _("Failed to execute gpg: %s"),
 
1804
                                      errno ? g_strerror (errno) : _("Unknown"));
 
1805
                goto fail;
 
1806
        }
 
1807
        
 
1808
        while (!gpg_ctx_op_complete (gpg)) {
 
1809
                if (gpg_ctx_op_step (gpg, ex) == -1)
 
1810
                        goto fail;
 
1811
        }
 
1812
        
 
1813
        if (gpg_ctx_op_wait (gpg) != 0) {
 
1814
                const char *diagnostics;
 
1815
                
 
1816
                diagnostics = gpg_ctx_get_diagnostics (gpg);
 
1817
                camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
 
1818
                                     diagnostics && *diagnostics ? diagnostics :
 
1819
                                     _("Failed to execute gpg."));
 
1820
                goto fail;
 
1821
        }
 
1822
 
 
1823
        res = 0;
 
1824
fail:
 
1825
        gpg_ctx_free (gpg);
 
1826
        
 
1827
        return res;
 
1828
}
 
1829
 
 
1830
static int
 
1831
gpg_export_keys (CamelCipherContext *context, GPtrArray *keys, CamelStream *ostream, CamelException *ex)
 
1832
{
 
1833
        struct _GpgCtx *gpg;
 
1834
        int i;
 
1835
        int res = -1;
 
1836
 
 
1837
        gpg = gpg_ctx_new (context->session);
 
1838
        gpg_ctx_set_mode (gpg, GPG_CTX_MODE_EXPORT);
 
1839
        gpg_ctx_set_armor (gpg, TRUE);
 
1840
        gpg_ctx_set_ostream (gpg, ostream);
 
1841
        
 
1842
        for (i = 0; i < keys->len; i++) {
 
1843
                gpg_ctx_add_recipient (gpg, keys->pdata[i]);
 
1844
        }
 
1845
        
 
1846
        if (gpg_ctx_op_start (gpg) == -1) {
 
1847
                camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
 
1848
                                      _("Failed to execute gpg: %s"),
 
1849
                                      errno ? g_strerror (errno) : _("Unknown"));
 
1850
                goto fail;
 
1851
        }
 
1852
        
 
1853
        while (!gpg_ctx_op_complete (gpg)) {
 
1854
                if (gpg_ctx_op_step (gpg, ex) == -1)
 
1855
                        goto fail;
 
1856
        }
 
1857
        
 
1858
        if (gpg_ctx_op_wait (gpg) != 0) {
 
1859
                const char *diagnostics;
 
1860
                
 
1861
                diagnostics = gpg_ctx_get_diagnostics (gpg);
 
1862
                camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
 
1863
                                     diagnostics && *diagnostics ? diagnostics :
 
1864
                                     _("Failed to execute gpg."));
 
1865
                goto fail;
 
1866
        }
 
1867
 
 
1868
        res = 0;
 
1869
fail:
 
1870
        gpg_ctx_free (gpg);
 
1871
        
 
1872
        return res;
 
1873
}
 
1874
 
 
1875
/* ********************************************************************** */
 
1876
 
 
1877
static void
 
1878
camel_gpg_context_class_init (CamelGpgContextClass *klass)
 
1879
{
 
1880
        CamelCipherContextClass *cipher_class = CAMEL_CIPHER_CONTEXT_CLASS (klass);
 
1881
        
 
1882
        parent_class = CAMEL_CIPHER_CONTEXT_CLASS (camel_type_get_global_classfuncs (camel_cipher_context_get_type ()));
 
1883
        
 
1884
        cipher_class->hash_to_id = gpg_hash_to_id;
 
1885
        cipher_class->id_to_hash = gpg_id_to_hash;
 
1886
        cipher_class->sign = gpg_sign;
 
1887
        cipher_class->verify = gpg_verify;
 
1888
        cipher_class->encrypt = gpg_encrypt;
 
1889
        cipher_class->decrypt = gpg_decrypt;
 
1890
        cipher_class->import_keys = gpg_import_keys;
 
1891
        cipher_class->export_keys = gpg_export_keys;
 
1892
}
 
1893
 
 
1894
static void
 
1895
camel_gpg_context_init (CamelGpgContext *context)
 
1896
{
 
1897
        CamelCipherContext *cipher = (CamelCipherContext *) context;
 
1898
        
 
1899
        context->always_trust = FALSE;
 
1900
        
 
1901
        cipher->sign_protocol = "application/pgp-signature";
 
1902
        cipher->encrypt_protocol = "application/pgp-encrypted";
 
1903
        cipher->key_protocol = "application/pgp-keys";
 
1904
}
 
1905
 
 
1906
static void
 
1907
camel_gpg_context_finalise (CamelObject *object)
 
1908
{
 
1909
        ;
 
1910
}
 
1911
 
 
1912
CamelType
 
1913
camel_gpg_context_get_type (void)
 
1914
{
 
1915
        static CamelType type = CAMEL_INVALID_TYPE;
 
1916
        
 
1917
        if (type == CAMEL_INVALID_TYPE) {
 
1918
                type = camel_type_register (camel_cipher_context_get_type (),
 
1919
                                            "CamelGpgContext",
 
1920
                                            sizeof (CamelGpgContext),
 
1921
                                            sizeof (CamelGpgContextClass),
 
1922
                                            (CamelObjectClassInitFunc) camel_gpg_context_class_init,
 
1923
                                            NULL,
 
1924
                                            (CamelObjectInitFunc) camel_gpg_context_init,
 
1925
                                            (CamelObjectFinalizeFunc) camel_gpg_context_finalise);
 
1926
        }
 
1927
        
 
1928
        return type;
 
1929
}
 
1930
 
 
1931