Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / camel-gpg-context.c
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 (status, "[GNUPG:] ", 9) != 0) {
751                 char *message;
752                 message = g_locale_to_utf8(status, -1, NULL, NULL, NULL);
753                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
754                                       _("Unexpected GnuPG status message encountered:\n\n%s"),
755                                       message);
756                 g_free(message);
757                 return -1;
758         }
759         
760         status += 9;
761         
762         if (!strncmp ((char *) status, "USERID_HINT ", 12)) {
763                 char *hint, *user;
764                 
765                 status += 12;
766                 status = (const unsigned char *) next_token ((char *) status, &hint);
767                 if (!hint) {
768                         camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
769                                              _("Failed to parse gpg userid hint."));
770                         return -1;
771                 }
772                 
773                 if (g_hash_table_lookup (gpg->userid_hint, hint)) {
774                         /* we already have this userid hint... */
775                         g_free (hint);
776                         goto recycle;
777                 }
778                 
779                 if (gpg->utf8 || !(user = g_locale_to_utf8 ((gchar *) status, -1, &nread, &nwritten, NULL)))
780                         user = g_strdup ((gchar *) status);
781                 
782                 g_strstrip (user);
783                 
784                 g_hash_table_insert (gpg->userid_hint, hint, user);
785         } else if (!strncmp ((char *) status, "NEED_PASSPHRASE ", 16)) {
786                 char *userid;
787                 
788                 status += 16;
789                 
790                 status = (const unsigned char *) next_token ((char *) status, &userid);
791                 if (!userid) {
792                         camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
793                                              _("Failed to parse gpg passphrase request."));
794                         return -1;
795                 }
796                 
797                 g_free (gpg->need_id);
798                 gpg->need_id = userid;
799         } else if (!strncmp ((char *) status, "NEED_PASSPHRASE_PIN ", 20)) {
800                 char *userid;
801                 
802                 status += 20;
803                 
804                 status = (const unsigned char *) next_token ((char *) status, &userid);
805                 if (!userid) {
806                         camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
807                                              _("Failed to parse gpg passphrase request."));
808                         return -1;
809                 }
810                 
811                 g_free (gpg->need_id);
812                 gpg->need_id = userid;
813         } else if (!strncmp ((char *) status, "GET_HIDDEN ", 11)) {
814                 const char *name = NULL;
815                 char *prompt, *passwd;
816                 guint32 flags;
817                 
818                 status += 11;
819                 
820                 if (gpg->need_id && !(name = g_hash_table_lookup (gpg->userid_hint, gpg->need_id)))
821                         name = gpg->need_id;
822                 else if (!name)
823                         name = "";
824                 
825                 if (!strncmp ((char *) status, "passphrase.pin.ask", 18)) {
826                         prompt = g_strdup_printf (_("You need a PIN to unlock the key for your\n"
827                                                     "SmartCard: \"%s\""), name);
828                 } else if (!strncmp ((char *) status, "passphrase.enter", 16)) {
829                         prompt = g_strdup_printf (_("You need a passphrase to unlock the key for\n"
830                                                     "user: \"%s\""), name);
831                 } else {
832                         next_token ((char *) status, &prompt);
833                         camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
834                                               _("Unexpected request from GnuPG for `%s'"), prompt);
835                         g_free (prompt);
836                         return -1;
837                 }
838                 
839                 flags = CAMEL_SESSION_PASSWORD_SECRET | CAMEL_SESSION_PASSPHRASE;
840                 if ((passwd = camel_session_get_password (gpg->session, NULL, NULL, prompt, gpg->need_id, flags, ex))) {
841                         if (!gpg->utf8) {
842                                 char *opasswd = passwd;
843                                 
844                                 if ((passwd = g_locale_to_utf8 (passwd, -1, &nread, &nwritten, NULL))) {
845                                         memset (opasswd, 0, strlen (opasswd));
846                                         g_free (opasswd);
847                                 } else {
848                                         passwd = opasswd;
849                                 }
850                         }
851                         
852                         gpg->passwd = g_strdup_printf ("%s\n", passwd);
853                         memset (passwd, 0, strlen (passwd));
854                         g_free (passwd);
855                         
856                         gpg->send_passwd = TRUE;
857                 } else {
858                         if (!camel_exception_is_set (ex))
859                                 camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, _("Canceled."));
860                         return -1;
861                 }
862                 
863                 g_free (prompt);
864         } else if (!strncmp ((char *) status, "GOOD_PASSPHRASE", 15)) {
865                 gpg->bad_passwds = 0;
866         } else if (!strncmp ((char *) status, "BAD_PASSPHRASE", 14)) {
867                 gpg->bad_passwds++;
868                 
869                 camel_session_forget_password (gpg->session, NULL, NULL, gpg->need_id, ex);
870                 
871                 if (gpg->bad_passwds == 3) {
872                         camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
873                                              _("Failed to unlock secret key: 3 bad passphrases given."));
874                         return -1;
875                 }
876         } else if (!strncmp (status, "UNEXPECTED ", 11)) {
877                 /* this is an error */
878                 char *message;
879                 message = g_locale_to_utf8(status+11, -1, NULL, NULL, NULL);
880                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
881                                       _("Unexpected response from GnuPG: %s"),
882                                       message);
883                 g_free(message);
884                 return -1;
885         } else if (!strncmp (status, "NODATA", 6)) {
886                 /* this is an error */
887                 /* But we ignore it anyway, we should get other response codes to say why */
888                 gpg->nodata = TRUE;
889         } else {
890                 /* check to see if we are complete */
891                 switch (gpg->mode) {
892                 case GPG_CTX_MODE_SIGN:
893                         if (!strncmp ((char *) status, "SIG_CREATED ", 12)) {
894                                 /* FIXME: save this state? */
895                         }
896                         break;
897                 case GPG_CTX_MODE_VERIFY:
898                         if (!strncmp ((char *) status, "TRUST_", 6)) {
899                                 status += 6;
900                                 if (!strncmp ((char *) status, "NEVER", 5)) {
901                                         gpg->trust = GPG_TRUST_NEVER;
902                                 } else if (!strncmp ((char *) status, "MARGINAL", 8)) {
903                                         gpg->trust = GPG_TRUST_MARGINAL;
904                                 } else if (!strncmp ((char *) status, "FULLY", 5)) {
905                                         gpg->trust = GPG_TRUST_FULLY;
906                                 } else if (!strncmp ((char *) status, "ULTIMATE", 8)) {
907                                         gpg->trust = GPG_TRUST_ULTIMATE;
908                                 } else if (!strncmp ((char *) status, "UNDEFINED", 9)) {
909                                         gpg->trust = GPG_TRUST_UNDEFINED;
910                                 }
911                         } else if (!strncmp ((char *) status, "GOODSIG ", 8)) {
912                                 gpg->goodsig = TRUE;
913                                 gpg->hadsig = TRUE;
914                         } else if (!strncmp ((char *) status, "VALIDSIG ", 9)) {
915                                 gpg->validsig = TRUE;
916                         } else if (!strncmp ((char *) status, "BADSIG ", 7)) {
917                                 gpg->badsig = FALSE;
918                                 gpg->hadsig = TRUE;
919                         } else if (!strncmp ((char *) status, "ERRSIG ", 7)) {
920                                 /* Note: NO_PUBKEY often comes after an ERRSIG */
921                                 gpg->errsig = FALSE;
922                                 gpg->hadsig = TRUE;
923                         } else if (!strncmp ((char *) status, "NO_PUBKEY ", 10)) {
924                                 gpg->nopubkey = TRUE;
925                         }
926                         break;
927                 case GPG_CTX_MODE_ENCRYPT:
928                         if (!strncmp ((char *) status, "BEGIN_ENCRYPTION", 16)) {
929                                 /* nothing to do... but we know to expect data on stdout soon */
930                         } else if (!strncmp ((char *) status, "END_ENCRYPTION", 14)) {
931                                 /* nothing to do, but we know the end is near? */
932                         } else if (!strncmp ((char *) status, "NO_RECP", 7)) {
933                                 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
934                                                      _("Failed to encrypt: No valid recipients specified."));
935                                 return -1;
936                         }
937                         break;
938                 case GPG_CTX_MODE_DECRYPT:
939                         if (!strncmp ((char *) status, "BEGIN_DECRYPTION", 16)) {
940                                 /* nothing to do... but we know to expect data on stdout soon */
941                         } else if (!strncmp ((char *) status, "END_DECRYPTION", 14)) {
942                                 /* nothing to do, but we know the end is near? */
943                         }
944                         break;
945                 case GPG_CTX_MODE_IMPORT:
946                         /* noop */
947                         break;
948                 case GPG_CTX_MODE_EXPORT:
949                         /* noop */
950                         break;
951                 }
952         }
953         
954  recycle:
955         
956         /* recycle our statusbuf by moving inptr to the beginning of statusbuf */
957         len = gpg->statusptr - inptr;
958         memmove (gpg->statusbuf, inptr, len);
959         
960         len = inptr - gpg->statusbuf;
961         gpg->statusleft += len;
962         gpg->statusptr -= len;
963         
964         /* if we have more data, try parsing the next line? */
965         if (gpg->statusptr > gpg->statusbuf)
966                 goto parse;
967         
968         return 0;
969 }
970
971 #endif
972
973 #define status_backup(gpg, start, len) G_STMT_START {                     \
974         if (gpg->statusleft <= len) {                                     \
975                 unsigned int slen, soff;                                  \
976                                                                           \
977                 slen = soff = gpg->statusptr - gpg->statusbuf;            \
978                 slen = slen ? slen : 1;                                   \
979                                                                           \
980                 while (slen < soff + len)                                 \
981                         slen <<= 1;                                       \
982                                                                           \
983                 gpg->statusbuf = g_realloc (gpg->statusbuf, slen + 1);    \
984                 gpg->statusptr = gpg->statusbuf + soff;                   \
985                 gpg->statusleft = slen - soff;                            \
986         }                                                                 \
987                                                                           \
988         memcpy (gpg->statusptr, start, len);                              \
989         gpg->statusptr += len;                                            \
990         gpg->statusleft -= len;                                           \
991 } G_STMT_END
992
993 #ifndef G_OS_WIN32
994
995 static void
996 gpg_ctx_op_cancel (struct _GpgCtx *gpg)
997 {
998         pid_t retval;
999         int status;
1000         
1001         if (gpg->exited)
1002                 return;
1003         
1004         kill (gpg->pid, SIGTERM);
1005         sleep (1);
1006         retval = waitpid (gpg->pid, &status, WNOHANG);
1007         if (retval == (pid_t) 0) {
1008                 /* no more mr nice guy... */
1009                 kill (gpg->pid, SIGKILL);
1010                 sleep (1);
1011                 waitpid (gpg->pid, &status, WNOHANG);
1012         }
1013 }
1014
1015 #endif
1016
1017 static int
1018 gpg_ctx_op_step (struct _GpgCtx *gpg, CamelException *ex)
1019 {
1020 #ifndef G_OS_WIN32
1021         struct pollfd polls[6];
1022         int status, i, cancel_fd;
1023
1024         for (i=0;i<6;i++) {
1025                 polls[i].fd = -1;
1026                 polls[i].events = 0;
1027         }
1028
1029         if (!gpg->seen_eof1) {
1030                 polls[0].fd = gpg->stdout_fd;
1031                 polls[0].events = POLLIN;
1032         }
1033
1034         if (!gpg->seen_eof2) {
1035                 polls[1].fd = gpg->stderr_fd;
1036                 polls[1].events = POLLIN;
1037         }
1038
1039         if (!gpg->complete) {
1040                 polls[2].fd = gpg->status_fd;
1041                 polls[2].events = POLLIN;
1042         }
1043
1044         polls[3].fd = gpg->stdin_fd;
1045         polls[3].events = POLLOUT;
1046         polls[4].fd = gpg->passwd_fd;
1047         polls[4].events = POLLOUT;
1048         cancel_fd = camel_operation_cancel_fd(NULL);
1049         polls[5].fd = cancel_fd;
1050         polls[5].events = POLLIN;
1051
1052         do {
1053                 for (i=0;i<6;i++)
1054                         polls[i].revents = 0;
1055                 status = poll(polls, 6, 30*1000);
1056         } while (status == -1 && errno == EINTR);
1057
1058         if (status == 0)
1059                 return 0;       /* timed out */
1060         else if (status == -1)
1061                 goto exception;
1062
1063         if ((polls[5].revents & POLLIN) && camel_operation_cancel_check(NULL)) {
1064                 camel_exception_set(ex, CAMEL_EXCEPTION_USER_CANCEL, _("Canceled."));
1065                 gpg_ctx_op_cancel(gpg);
1066                 return -1;
1067         }
1068
1069         /* Test each and every file descriptor to see if it's 'ready',
1070            and if so - do what we can with it and then drop through to
1071            the next file descriptor and so on until we've done what we
1072            can to all of them. If one fails along the way, return
1073            -1. */
1074         
1075         if (polls[2].revents & (POLLIN|POLLHUP)) {
1076                 /* read the status message and decide what to do... */
1077                 char buffer[4096];
1078                 ssize_t nread;
1079                 
1080                 d(printf ("reading from gpg's status-fd...\n"));
1081                 
1082                 do {
1083                         nread = read (gpg->status_fd, buffer, sizeof (buffer));
1084                 } while (nread == -1 && (errno == EINTR || errno == EAGAIN));
1085                 if (nread == -1)
1086                         goto exception;
1087                 
1088                 if (nread > 0) {
1089                         status_backup (gpg, buffer, nread);
1090                         
1091                         if (gpg_ctx_parse_status (gpg, ex) == -1)
1092                                 return -1;
1093                 } else {
1094                         gpg->complete = TRUE;
1095                 }
1096         }
1097         
1098         if ((polls[0].revents & (POLLIN|POLLHUP)) && gpg->ostream) {
1099                 char buffer[4096];
1100                 ssize_t nread;
1101                 
1102                 d(printf ("reading gpg's stdout...\n"));
1103                 
1104                 do {
1105                         nread = read (gpg->stdout_fd, buffer, sizeof (buffer));
1106                 } while (nread == -1 && (errno == EINTR || errno == EAGAIN));
1107                 if (nread == -1)
1108                         goto exception;
1109                 
1110                 if (nread > 0) {
1111                         if (camel_stream_write (gpg->ostream, buffer, (size_t) nread) == -1)
1112                                 goto exception;
1113                 } else {
1114                         gpg->seen_eof1 = TRUE;
1115                 }
1116         }
1117         
1118         if (polls[1].revents & (POLLIN|POLLHUP)) {
1119                 char buffer[4096];
1120                 ssize_t nread;
1121                 
1122                 d(printf ("reading gpg's stderr...\n"));
1123                 
1124                 do {
1125                         nread = read (gpg->stderr_fd, buffer, sizeof (buffer));
1126                 } while (nread == -1 && (errno == EINTR || errno == EAGAIN));
1127                 if (nread == -1)
1128                         goto exception;
1129                 
1130                 if (nread > 0) {
1131                         camel_stream_write (gpg->diagnostics, buffer, nread);
1132                 } else {
1133                         gpg->seen_eof2 = TRUE;
1134                 }
1135         }
1136         
1137         if ((polls[4].revents & (POLLOUT|POLLHUP)) && gpg->need_passwd && gpg->send_passwd) {
1138                 ssize_t w, nwritten = 0;
1139                 size_t n;
1140                 
1141                 d(printf ("sending gpg our passphrase...\n"));
1142                 
1143                 /* send the passphrase to gpg */
1144                 n = strlen (gpg->passwd);
1145                 do {
1146                         do {
1147                                 w = write (gpg->passwd_fd, gpg->passwd + nwritten, n - nwritten);
1148                         } while (w == -1 && (errno == EINTR || errno == EAGAIN));
1149                         
1150                         if (w > 0)
1151                                 nwritten += w;
1152                 } while (nwritten < n && w != -1);
1153                 
1154                 /* zero and free our passwd buffer */
1155                 memset (gpg->passwd, 0, n);
1156                 g_free (gpg->passwd);
1157                 gpg->passwd = NULL;
1158                 
1159                 if (w == -1)
1160                         goto exception;
1161                 
1162                 gpg->send_passwd = FALSE;
1163         }
1164         
1165         if ((polls[3].revents & (POLLOUT|POLLHUP)) && gpg->istream) {
1166                 char buffer[4096];
1167                 ssize_t nread;
1168                 
1169                 d(printf ("writing to gpg's stdin...\n"));
1170                 
1171                 /* write our stream to gpg's stdin */
1172                 nread = camel_stream_read (gpg->istream, buffer, sizeof (buffer));
1173                 if (nread > 0) {
1174                         ssize_t w, nwritten = 0;
1175                         
1176                         do {
1177                                 do {
1178                                         w = write (gpg->stdin_fd, buffer + nwritten, nread - nwritten);
1179                                 } while (w == -1 && (errno == EINTR || errno == EAGAIN));
1180                                 
1181                                 if (w > 0)
1182                                         nwritten += w;
1183                         } while (nwritten < nread && w != -1);
1184                                                 
1185                         if (w == -1)
1186                                 goto exception;
1187                         
1188                         d(printf ("wrote %d (out of %d) bytes to gpg's stdin\n", nwritten, nread));
1189                 }
1190                 
1191                 if (camel_stream_eos (gpg->istream)) {
1192                         d(printf ("closing gpg's stdin\n"));
1193                         close (gpg->stdin_fd);
1194                         gpg->stdin_fd = -1;
1195                 }
1196         }
1197         
1198         return 0;
1199         
1200  exception:
1201         /* always called on an i/o error */
1202         camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Failed to execute gpg: %s"), g_strerror(errno));
1203         gpg_ctx_op_cancel(gpg);
1204 #endif
1205         return -1;
1206 }
1207
1208 static gboolean
1209 gpg_ctx_op_complete (struct _GpgCtx *gpg)
1210 {
1211         return gpg->complete && gpg->seen_eof1 && gpg->seen_eof2;}
1212
1213
1214 #if 0
1215 static gboolean
1216 gpg_ctx_op_exited (struct _GpgCtx *gpg)
1217 {
1218         pid_t retval;
1219         int status;
1220         
1221         if (gpg->exited)
1222                 return TRUE;
1223         
1224         retval = waitpid (gpg->pid, &status, WNOHANG);
1225         if (retval == gpg->pid) {
1226                 gpg->exit_status = status;
1227                 gpg->exited = TRUE;
1228                 return TRUE;
1229         }
1230         
1231         return FALSE;
1232 }
1233 #endif
1234
1235 static int
1236 gpg_ctx_op_wait (struct _GpgCtx *gpg)
1237 {
1238 #ifndef G_OS_WIN32
1239         sigset_t mask, omask;
1240         pid_t retval;
1241         int status;
1242         
1243         if (!gpg->exited) {
1244                 sigemptyset (&mask);
1245                 sigaddset (&mask, SIGALRM);
1246                 sigprocmask (SIG_BLOCK, &mask, &omask);
1247                 alarm (1);
1248                 retval = waitpid (gpg->pid, &status, 0);
1249                 alarm (0);
1250                 sigprocmask (SIG_SETMASK, &omask, NULL);
1251                 
1252                 if (retval == (pid_t) -1 && errno == EINTR) {
1253                         /* The child is hanging: send a friendly reminder. */
1254                         kill (gpg->pid, SIGTERM);
1255                         sleep (1);
1256                         retval = waitpid (gpg->pid, &status, WNOHANG);
1257                         if (retval == (pid_t) 0) {
1258                                 /* Still hanging; use brute force. */
1259                                 kill (gpg->pid, SIGKILL);
1260                                 sleep (1);
1261                                 retval = waitpid (gpg->pid, &status, WNOHANG);
1262                         }
1263                 }
1264         } else {
1265                 status = gpg->exit_status;
1266                 retval = gpg->pid;
1267         }
1268         
1269         if (retval != (pid_t) -1 && WIFEXITED (status))
1270                 return WEXITSTATUS (status);
1271         else
1272                 return -1;
1273 #else
1274         return -1;
1275 #endif
1276 }
1277
1278
1279
1280 static int
1281 gpg_sign (CamelCipherContext *context, const char *userid, CamelCipherHash hash, CamelMimePart *ipart, CamelMimePart *opart, CamelException *ex)
1282 {
1283         struct _GpgCtx *gpg = NULL;
1284         CamelStream *ostream = camel_stream_mem_new(), *istream;
1285         CamelDataWrapper *dw;
1286         CamelContentType *ct;
1287         int res = -1;
1288         CamelMimePart *sigpart;
1289         CamelMultipartSigned *mps;
1290         
1291         /* Note: see rfc2015 or rfc3156, section 5 */
1292         
1293         /* FIXME: stream this, we stream output at least */
1294         istream = camel_stream_mem_new();
1295         if (camel_cipher_canonical_to_stream(ipart, CAMEL_MIME_FILTER_CANON_STRIP|CAMEL_MIME_FILTER_CANON_CRLF|CAMEL_MIME_FILTER_CANON_FROM,
1296                                              istream) == -1) {
1297                 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
1298                                      _("Could not generate signing data: %s"), g_strerror(errno));
1299                 goto fail;
1300         }
1301
1302 #ifdef GPG_LOG
1303         if (camel_debug_start("gpg:sign")) {
1304                 char *name;
1305                 CamelStream *out;
1306
1307                 name = g_strdup_printf("camel-gpg.%d.sign-data", logid++);
1308                 out = camel_stream_fs_new_with_name(name, O_CREAT|O_TRUNC|O_WRONLY, 0666);
1309                 if (out) {
1310                         printf("Writing gpg signing data to '%s'\n", name);
1311                         camel_stream_write_to_stream(istream, out);
1312                         camel_stream_reset(istream);
1313                         camel_object_unref(out);
1314                 }
1315                 g_free(name);
1316                 camel_debug_end();
1317         }
1318 #endif
1319
1320         gpg = gpg_ctx_new (context->session);
1321         gpg_ctx_set_mode (gpg, GPG_CTX_MODE_SIGN);
1322         gpg_ctx_set_hash (gpg, hash);
1323         gpg_ctx_set_armor (gpg, TRUE);
1324         gpg_ctx_set_userid (gpg, userid);
1325         gpg_ctx_set_istream (gpg, istream);
1326         gpg_ctx_set_ostream (gpg, ostream);
1327         
1328         if (gpg_ctx_op_start (gpg) == -1) {
1329                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1330                                       _("Failed to execute gpg: %s"), g_strerror (errno));
1331                 goto fail;
1332         }
1333         
1334         while (!gpg_ctx_op_complete (gpg)) {
1335                 if (gpg_ctx_op_step (gpg, ex) == -1)
1336                         goto fail;
1337         }
1338         
1339         if (gpg_ctx_op_wait (gpg) != 0) {
1340                 const char *diagnostics;
1341                 
1342                 diagnostics = gpg_ctx_get_diagnostics (gpg);
1343                 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
1344                                      diagnostics && *diagnostics ? diagnostics :
1345                                      _("Failed to execute gpg."));
1346                 goto fail;
1347         }
1348
1349         res = 0;
1350
1351         dw = camel_data_wrapper_new();
1352         camel_stream_reset(ostream);
1353         camel_data_wrapper_construct_from_stream(dw, ostream);
1354
1355         sigpart = camel_mime_part_new();
1356         ct = camel_content_type_new("application", "pgp-signature");
1357         camel_content_type_set_param(ct, "name", "signature.asc");
1358         camel_data_wrapper_set_mime_type_field(dw, ct);
1359         camel_content_type_unref(ct);
1360
1361         camel_medium_set_content_object((CamelMedium *)sigpart, dw);
1362         camel_object_unref(dw);
1363
1364         camel_mime_part_set_description(sigpart, _("This is a digitally signed message part"));
1365
1366         mps = camel_multipart_signed_new();
1367         ct = camel_content_type_new("multipart", "signed");
1368         camel_content_type_set_param(ct, "micalg", camel_cipher_hash_to_id(context, hash));
1369         camel_content_type_set_param(ct, "protocol", context->sign_protocol);
1370         camel_data_wrapper_set_mime_type_field((CamelDataWrapper *)mps, ct);
1371         camel_content_type_unref(ct);
1372         camel_multipart_set_boundary((CamelMultipart *)mps, NULL);
1373
1374         mps->signature = sigpart;
1375         mps->contentraw = istream;
1376         camel_stream_reset(istream);
1377         camel_object_ref(istream);
1378
1379         camel_medium_set_content_object((CamelMedium *)opart, (CamelDataWrapper *)mps);
1380 fail:
1381         camel_object_unref(ostream);
1382         
1383         if (gpg)
1384                 gpg_ctx_free (gpg);
1385         
1386         return res;
1387 }
1388
1389
1390 static char *
1391 swrite (CamelMimePart *sigpart)
1392 {
1393         CamelStream *ostream;
1394         char *template;
1395         int fd, ret;
1396         
1397         template = g_build_filename (g_get_tmp_dir (), "evolution-pgp.XXXXXX", NULL);
1398         if ((fd = g_mkstemp (template)) == -1) {
1399                 g_free (template);
1400                 return NULL;
1401         }
1402         
1403         /* TODO: This should probably just write the decoded message content out, not the part + headers */
1404         
1405         ostream = camel_stream_fs_new_with_fd (fd);
1406         ret = camel_data_wrapper_write_to_stream((CamelDataWrapper *)sigpart, ostream);
1407         if (ret != -1) {
1408                 ret = camel_stream_flush (ostream);
1409                 if (ret != -1)
1410                         ret = camel_stream_close (ostream);
1411         }
1412         
1413         camel_object_unref(ostream);
1414         
1415         if (ret == -1) {
1416                 g_unlink (template);
1417                 g_free (template);
1418                 return NULL;
1419         }
1420         
1421         return template;
1422 }
1423
1424 static CamelCipherValidity *
1425 gpg_verify (CamelCipherContext *context, CamelMimePart *ipart, CamelException *ex)
1426 {
1427         CamelCipherValidity *validity;
1428         const char *diagnostics = NULL;
1429         struct _GpgCtx *gpg = NULL;
1430         char *sigfile = NULL;
1431         CamelContentType *ct;
1432         CamelMimePart *sigpart;
1433         CamelStream *istream = NULL;
1434         CamelMultipart *mps;
1435
1436         mps = (CamelMultipart *)camel_medium_get_content_object((CamelMedium *)ipart);
1437         ct = ((CamelDataWrapper *)mps)->mime_type;
1438         
1439         /* Inline signature (using our fake mime type) or PGP/Mime signature */
1440         if (camel_content_type_is(ct, "multipart", "signed")) {
1441                 /* PGP/Mime Signature */
1442                 const char *tmp;
1443
1444                 tmp = camel_content_type_param(ct, "protocol");
1445                 if (!CAMEL_IS_MULTIPART_SIGNED(mps)
1446                     || tmp == NULL  
1447                     || g_ascii_strcasecmp(tmp, context->sign_protocol) != 0) {
1448                         camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1449                                       _("Cannot verify message signature: Incorrect message format"));
1450                         return NULL;
1451                 }
1452         
1453                 if (!(istream = camel_multipart_signed_get_content_stream ((CamelMultipartSigned *) mps, NULL))) {
1454                         camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1455                                       _("Cannot verify message signature: Incorrect message format"));
1456                         return NULL;
1457                 }
1458         
1459                 if (!(sigpart = camel_multipart_get_part (mps, CAMEL_MULTIPART_SIGNED_SIGNATURE))) {
1460                         camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1461                                       _("Cannot verify message signature: Incorrect message format"));
1462                         camel_object_unref (istream);
1463                         return NULL;
1464                 }
1465
1466         } else if (camel_content_type_is(ct, "application", "x-inlinepgp-signed")) {
1467                 /* Inline Signed */
1468                 CamelDataWrapper *content;
1469                 content = camel_medium_get_content_object ((CamelMedium *) ipart);
1470                 istream = camel_stream_mem_new();
1471                 camel_data_wrapper_decode_to_stream (content, istream);
1472                 camel_stream_reset(istream);
1473                 sigpart = NULL;
1474         } else {
1475                 /* Invalid Mimetype */
1476                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1477                               _("Cannot verify message signature: Incorrect message format"));
1478                 return NULL;
1479         }
1480         
1481         /* Now start the real work of verifying the message */
1482 #ifdef GPG_LOG
1483         if (camel_debug_start("gpg:sign")) {
1484                 char *name;
1485                 CamelStream *out;
1486
1487                 name = g_strdup_printf("camel-gpg.%d.verify.data", logid);
1488                 out = camel_stream_fs_new_with_name(name, O_CREAT|O_TRUNC|O_WRONLY, 0666);
1489                 if (out) {
1490                         printf("Writing gpg verify data to '%s'\n", name);
1491                         camel_stream_write_to_stream(istream, out);
1492                         camel_stream_reset(istream);
1493                         camel_object_unref(out);
1494                 }
1495                 g_free(name);
1496                 if (sigpart) {
1497                         name = g_strdup_printf("camel-gpg.%d.verify.signature", logid++);
1498                         out = camel_stream_fs_new_with_name(name, O_CREAT|O_TRUNC|O_WRONLY, 0666);
1499                         if (out) {
1500                                 printf("Writing gpg verify signature to '%s'\n", name);
1501                                 camel_data_wrapper_write_to_stream((CamelDataWrapper *)sigpart, out);
1502                                 camel_object_unref(out);
1503                         }
1504                         g_free(name);
1505                 }
1506                 camel_debug_end();
1507         }
1508 #endif
1509
1510         if (sigpart) {
1511                 sigfile = swrite (sigpart);
1512                 if (sigfile == NULL) {
1513                         camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1514                                       _("Cannot verify message signature: could not create temp file: %s"),
1515                                       g_strerror (errno));
1516                         goto exception;
1517                 }
1518         }
1519         
1520         camel_stream_reset(istream);
1521         gpg = gpg_ctx_new (context->session);
1522         gpg_ctx_set_mode (gpg, GPG_CTX_MODE_VERIFY);
1523         if (sigfile)
1524                 gpg_ctx_set_sigfile (gpg, sigfile);
1525         gpg_ctx_set_istream (gpg, istream);
1526         
1527         if (gpg_ctx_op_start (gpg) == -1) {
1528                 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
1529                                      _("Failed to execute gpg."));
1530                 goto exception;
1531         }
1532         
1533         while (!gpg_ctx_op_complete (gpg)) {
1534                 if (gpg_ctx_op_step (gpg, ex) == -1)
1535                         goto exception;
1536         }
1537         
1538         gpg_ctx_op_wait (gpg);
1539         validity = camel_cipher_validity_new ();
1540         diagnostics = gpg_ctx_get_diagnostics (gpg);
1541         camel_cipher_validity_set_description (validity, diagnostics);
1542         if (gpg->validsig) {
1543                 if (gpg->trust == GPG_TRUST_UNDEFINED || gpg->trust == GPG_TRUST_NONE)
1544                         validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_UNKNOWN;
1545                 else if (gpg->trust != GPG_TRUST_NEVER)
1546                         validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_GOOD;
1547                 else
1548                         validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
1549         } else if (gpg->nopubkey) {
1550                 validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_NEED_PUBLIC_KEY;
1551         } else {
1552                 validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
1553         }
1554         
1555         gpg_ctx_free (gpg);
1556         
1557         if (sigfile) {
1558                 g_unlink (sigfile);
1559                 g_free (sigfile);
1560         }
1561         camel_object_unref(istream);
1562         
1563         return validity;
1564         
1565  exception:
1566         
1567         if (gpg != NULL)
1568                 gpg_ctx_free (gpg);
1569         
1570         if (istream)
1571                 camel_object_unref(istream);
1572
1573         if (sigfile) {
1574                 g_unlink (sigfile);
1575                 g_free (sigfile);
1576         }
1577         
1578         return NULL;
1579 }
1580
1581 static int
1582 gpg_encrypt (CamelCipherContext *context, const char *userid, GPtrArray *recipients, struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex)
1583 {
1584         CamelGpgContext *ctx = (CamelGpgContext *) context;
1585         struct _GpgCtx *gpg;
1586         int i, res = -1;
1587         CamelStream *istream, *ostream, *vstream;
1588         CamelMimePart *encpart, *verpart;
1589         CamelDataWrapper *dw;
1590         CamelContentType *ct;
1591         CamelMultipartEncrypted *mpe;
1592
1593         ostream = camel_stream_mem_new();
1594         istream = camel_stream_mem_new();
1595         if (camel_cipher_canonical_to_stream(ipart, CAMEL_MIME_FILTER_CANON_CRLF, istream) == -1) {
1596                 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
1597                                      _("Could not generate encrypting data: %s"), g_strerror(errno));
1598                 goto fail1;
1599         }
1600
1601         gpg = gpg_ctx_new (context->session);
1602         gpg_ctx_set_mode (gpg, GPG_CTX_MODE_ENCRYPT);
1603         gpg_ctx_set_armor (gpg, TRUE);
1604         gpg_ctx_set_userid (gpg, userid);
1605         gpg_ctx_set_istream (gpg, istream);
1606         gpg_ctx_set_ostream (gpg, ostream);
1607         gpg_ctx_set_always_trust (gpg, ctx->always_trust);
1608
1609         for (i = 0; i < recipients->len; i++) {
1610                 gpg_ctx_add_recipient (gpg, recipients->pdata[i]);
1611         }
1612         
1613         if (gpg_ctx_op_start (gpg) == -1) {
1614                 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Failed to execute gpg."));
1615                 goto fail;
1616         }
1617
1618         /* FIXME: move tihs to a common routine */
1619         while (!gpg_ctx_op_complete(gpg)) {
1620                 if (gpg_ctx_op_step (gpg, ex) == -1)
1621                         goto fail;
1622         }
1623         
1624         if (gpg_ctx_op_wait (gpg) != 0) {
1625                 const char *diagnostics;
1626                 
1627                 diagnostics = gpg_ctx_get_diagnostics (gpg);
1628                 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
1629                                      diagnostics && *diagnostics ? diagnostics : _("Failed to execute gpg."));
1630                 goto fail;
1631         }
1632
1633         res = 0;
1634
1635         dw = camel_data_wrapper_new();
1636         camel_data_wrapper_construct_from_stream(dw, ostream);
1637
1638         encpart = camel_mime_part_new();
1639         ct = camel_content_type_new("application", "octet-stream");
1640         camel_content_type_set_param(ct, "name", "encrypted.asc");
1641         camel_data_wrapper_set_mime_type_field(dw, ct);
1642         camel_content_type_unref(ct);
1643
1644         camel_medium_set_content_object((CamelMedium *)encpart, dw);
1645         camel_object_unref(dw);
1646
1647         camel_mime_part_set_description(encpart, _("This is a digitally encrypted message part"));
1648
1649         vstream = camel_stream_mem_new();
1650         camel_stream_write(vstream, "Version: 1\n", strlen("Version: 1\n"));
1651         camel_stream_reset(vstream);
1652
1653         verpart = camel_mime_part_new();
1654         dw = camel_data_wrapper_new();
1655         camel_data_wrapper_set_mime_type(dw, context->encrypt_protocol);
1656         camel_data_wrapper_construct_from_stream(dw, vstream);
1657         camel_object_unref(vstream);
1658         camel_medium_set_content_object((CamelMedium *)verpart, dw);
1659         camel_object_unref(dw);
1660
1661         mpe = camel_multipart_encrypted_new();
1662         ct = camel_content_type_new("multipart", "encrypted");
1663         camel_content_type_set_param(ct, "protocol", context->encrypt_protocol);
1664         camel_data_wrapper_set_mime_type_field((CamelDataWrapper *)mpe, ct);
1665         camel_content_type_unref(ct);
1666         camel_multipart_set_boundary((CamelMultipart *)mpe, NULL);
1667
1668         mpe->decrypted = ipart;
1669         camel_object_ref(ipart);
1670
1671         camel_multipart_add_part((CamelMultipart *)mpe, verpart);
1672         camel_object_unref(verpart);
1673         camel_multipart_add_part((CamelMultipart *)mpe, encpart);
1674         camel_object_unref(encpart);
1675
1676         camel_medium_set_content_object((CamelMedium *)opart, (CamelDataWrapper *)mpe);
1677 fail:
1678         gpg_ctx_free(gpg);
1679 fail1:
1680         camel_object_unref(istream);
1681         camel_object_unref(ostream);
1682                 
1683         return res;
1684 }
1685
1686 static CamelCipherValidity *
1687 gpg_decrypt(CamelCipherContext *context, CamelMimePart *ipart, CamelMimePart *opart, CamelException *ex)
1688 {
1689         struct _GpgCtx *gpg;
1690         CamelCipherValidity *valid = NULL;
1691         CamelStream *ostream, *istream;
1692         CamelDataWrapper *content;
1693         CamelMimePart *encrypted;
1694         CamelMultipart *mp;
1695         CamelContentType *ct;
1696         int rv;
1697
1698         if (!ipart) {
1699                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1700                                 _("Cannot decrypt message: Incorrect message format"));
1701                 return NULL;
1702         }
1703
1704         content = camel_medium_get_content_object((CamelMedium *)ipart);
1705
1706         if (!content) {
1707                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1708                                 _("Cannot decrypt message: Incorrect message format"));
1709                 return NULL;
1710         }
1711
1712         ct = camel_mime_part_get_content_type((CamelMimePart *)content);
1713         /* Encrypted part (using our fake mime type) or PGP/Mime multipart */
1714         if (camel_content_type_is(ct, "multipart", "encrypted")) {      
1715                 mp = (CamelMultipart *) camel_medium_get_content_object ((CamelMedium *) ipart);
1716                 if (!(encrypted = camel_multipart_get_part (mp, CAMEL_MULTIPART_ENCRYPTED_CONTENT))) {
1717                         camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Failed to decrypt MIME part: protocol error"));
1718                         return NULL;
1719                 }
1720
1721                 content = camel_medium_get_content_object ((CamelMedium *) encrypted);
1722         } else if (camel_content_type_is(ct, "application", "x-inlinepgp-encrypted")) {
1723                 content = camel_medium_get_content_object ((CamelMedium *) ipart);
1724
1725         } else {
1726                 /* Invalid Mimetype */
1727                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1728                                 _("Cannot decrypt message: Incorrect message format"));
1729                 return NULL;
1730         }
1731         
1732         istream = camel_stream_mem_new();
1733         camel_data_wrapper_decode_to_stream (content, istream);
1734         camel_stream_reset(istream);
1735         
1736         ostream = camel_stream_mem_new();
1737         camel_stream_mem_set_secure((CamelStreamMem *)ostream);
1738
1739         gpg = gpg_ctx_new (context->session);
1740         gpg_ctx_set_mode (gpg, GPG_CTX_MODE_DECRYPT);
1741         gpg_ctx_set_istream (gpg, istream);
1742         gpg_ctx_set_ostream (gpg, ostream);
1743         
1744         if (gpg_ctx_op_start (gpg) == -1) {
1745                 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
1746                                      _("Failed to execute gpg."));
1747                 goto fail;
1748         }
1749         
1750         while (!gpg_ctx_op_complete (gpg)) {
1751                 if (gpg_ctx_op_step (gpg, ex) == -1)
1752                         goto fail;
1753         }
1754         
1755         if (gpg_ctx_op_wait (gpg) != 0) {
1756                 const char *diagnostics;
1757                 
1758                 diagnostics = gpg_ctx_get_diagnostics (gpg);
1759                 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
1760                                      diagnostics && *diagnostics ? diagnostics :
1761                                      _("Failed to execute gpg."));
1762                 goto fail;
1763         }
1764
1765         camel_stream_reset(ostream);
1766         if (camel_content_type_is(ct, "multipart", "encrypted")) {
1767                 /* Multipart encrypted - parse a full mime part */
1768                 rv = camel_data_wrapper_construct_from_stream((CamelDataWrapper *)opart, ostream);
1769         } else {
1770                 /* Inline signed - raw data (may not be a mime part) */
1771                 CamelDataWrapper *dw;
1772                 dw = camel_data_wrapper_new ();
1773                 rv = camel_data_wrapper_construct_from_stream(dw, ostream);
1774                 camel_data_wrapper_set_mime_type(dw, "application/octet-stream");
1775                 camel_medium_set_content_object((CamelMedium *)opart, dw);
1776                 camel_object_unref(dw);
1777                 /* Set mime/type of this new part to application/octet-stream to force type snooping */
1778                 camel_mime_part_set_content_type(opart, "application/octet-stream");
1779         }
1780         if (rv != -1) { 
1781                 valid = camel_cipher_validity_new();
1782                 valid->encrypt.description = g_strdup(_("Encrypted content"));
1783                 valid->encrypt.status = CAMEL_CIPHER_VALIDITY_ENCRYPT_ENCRYPTED;
1784                 
1785                 if (gpg->hadsig) {
1786                         if (gpg->validsig) {
1787                                 if (gpg->trust == GPG_TRUST_UNDEFINED || gpg->trust == GPG_TRUST_NONE)
1788                                         valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_UNKNOWN;
1789                                 else if (gpg->trust != GPG_TRUST_NEVER)
1790                                         valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_GOOD;
1791                                 else
1792                                         valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
1793                         } else if (gpg->nopubkey) {
1794                                 valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_NEED_PUBLIC_KEY;
1795                         } else {
1796                                 valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
1797                         }
1798                 }
1799         } else {
1800                 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
1801                                      _("Unable to parse message content"));
1802         }
1803         
1804  fail:
1805         camel_object_unref(ostream);
1806         camel_object_unref(istream);
1807         gpg_ctx_free (gpg);
1808
1809         return valid;
1810 }
1811
1812 static int
1813 gpg_import_keys (CamelCipherContext *context, CamelStream *istream, CamelException *ex)
1814 {
1815         struct _GpgCtx *gpg;
1816         int res = -1;
1817
1818         gpg = gpg_ctx_new (context->session);
1819         gpg_ctx_set_mode (gpg, GPG_CTX_MODE_IMPORT);
1820         gpg_ctx_set_istream (gpg, istream);
1821         
1822         if (gpg_ctx_op_start (gpg) == -1) {
1823                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1824                                       _("Failed to execute gpg: %s"),
1825                                       errno ? g_strerror (errno) : _("Unknown"));
1826                 goto fail;
1827         }
1828         
1829         while (!gpg_ctx_op_complete (gpg)) {
1830                 if (gpg_ctx_op_step (gpg, ex) == -1)
1831                         goto fail;
1832         }
1833         
1834         if (gpg_ctx_op_wait (gpg) != 0) {
1835                 const char *diagnostics;
1836                 
1837                 diagnostics = gpg_ctx_get_diagnostics (gpg);
1838                 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
1839                                      diagnostics && *diagnostics ? diagnostics :
1840                                      _("Failed to execute gpg."));
1841                 goto fail;
1842         }
1843
1844         res = 0;
1845 fail:
1846         gpg_ctx_free (gpg);
1847         
1848         return res;
1849 }
1850
1851 static int
1852 gpg_export_keys (CamelCipherContext *context, GPtrArray *keys, CamelStream *ostream, CamelException *ex)
1853 {
1854         struct _GpgCtx *gpg;
1855         int i;
1856         int res = -1;
1857
1858         gpg = gpg_ctx_new (context->session);
1859         gpg_ctx_set_mode (gpg, GPG_CTX_MODE_EXPORT);
1860         gpg_ctx_set_armor (gpg, TRUE);
1861         gpg_ctx_set_ostream (gpg, ostream);
1862         
1863         for (i = 0; i < keys->len; i++) {
1864                 gpg_ctx_add_recipient (gpg, keys->pdata[i]);
1865         }
1866         
1867         if (gpg_ctx_op_start (gpg) == -1) {
1868                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1869                                       _("Failed to execute gpg: %s"),
1870                                       errno ? g_strerror (errno) : _("Unknown"));
1871                 goto fail;
1872         }
1873         
1874         while (!gpg_ctx_op_complete (gpg)) {
1875                 if (gpg_ctx_op_step (gpg, ex) == -1)
1876                         goto fail;
1877         }
1878         
1879         if (gpg_ctx_op_wait (gpg) != 0) {
1880                 const char *diagnostics;
1881                 
1882                 diagnostics = gpg_ctx_get_diagnostics (gpg);
1883                 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
1884                                      diagnostics && *diagnostics ? diagnostics :
1885                                      _("Failed to execute gpg."));
1886                 goto fail;
1887         }
1888
1889         res = 0;
1890 fail:
1891         gpg_ctx_free (gpg);
1892         
1893         return res;
1894 }
1895
1896 /* ********************************************************************** */
1897
1898 static void
1899 camel_gpg_context_class_init (CamelGpgContextClass *klass)
1900 {
1901         CamelCipherContextClass *cipher_class = CAMEL_CIPHER_CONTEXT_CLASS (klass);
1902         
1903         parent_class = CAMEL_CIPHER_CONTEXT_CLASS (camel_type_get_global_classfuncs (camel_cipher_context_get_type ()));
1904         
1905         cipher_class->hash_to_id = gpg_hash_to_id;
1906         cipher_class->id_to_hash = gpg_id_to_hash;
1907         cipher_class->sign = gpg_sign;
1908         cipher_class->verify = gpg_verify;
1909         cipher_class->encrypt = gpg_encrypt;
1910         cipher_class->decrypt = gpg_decrypt;
1911         cipher_class->import_keys = gpg_import_keys;
1912         cipher_class->export_keys = gpg_export_keys;
1913 }
1914
1915 static void
1916 camel_gpg_context_init (CamelGpgContext *context)
1917 {
1918         CamelCipherContext *cipher = (CamelCipherContext *) context;
1919         
1920         context->always_trust = FALSE;
1921         
1922         cipher->sign_protocol = "application/pgp-signature";
1923         cipher->encrypt_protocol = "application/pgp-encrypted";
1924         cipher->key_protocol = "application/pgp-keys";
1925 }
1926
1927 static void
1928 camel_gpg_context_finalise (CamelObject *object)
1929 {
1930         ;
1931 }
1932
1933 CamelType
1934 camel_gpg_context_get_type (void)
1935 {
1936         static CamelType type = CAMEL_INVALID_TYPE;
1937         
1938         if (type == CAMEL_INVALID_TYPE) {
1939                 type = camel_type_register (camel_cipher_context_get_type (),
1940                                             "CamelGpgContext",
1941                                             sizeof (CamelGpgContext),
1942                                             sizeof (CamelGpgContextClass),
1943                                             (CamelObjectClassInitFunc) camel_gpg_context_class_init,
1944                                             NULL,
1945                                             (CamelObjectInitFunc) camel_gpg_context_init,
1946                                             (CamelObjectFinalizeFunc) camel_gpg_context_finalise);
1947         }
1948         
1949         return type;
1950 }
1951
1952