try again
[claws.git] / src / plugins / pgpmime / pgpmime.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2006 Hiroyuki Yamamoto & the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #ifdef USE_GPGME
25
26 #include "defs.h"
27 #include <glib.h>
28 #include <glib/gi18n.h>
29 #include <gpgme.h>
30 #include <ctype.h>
31 #include <errno.h>
32
33 #include "utils.h"
34 #include "privacy.h"
35 #include "procmime.h"
36 #include "plugin.h"
37
38 #include "pgpmime.h"
39 #include <plugins/pgpcore/sgpgme.h>
40 #include <plugins/pgpcore/prefs_gpg.h>
41 #include <plugins/pgpcore/passphrase.h>
42
43 #include "prefs_common.h"
44
45 typedef struct _PrivacyDataPGP PrivacyDataPGP;
46
47 struct _PrivacyDataPGP
48 {
49         PrivacyData     data;
50         
51         gboolean        done_sigtest;
52         gboolean        is_signed;
53         gpgme_verify_result_t   sigstatus;
54         gpgme_ctx_t     ctx;
55 };
56
57 static PrivacySystem pgpmime_system;
58
59 static gint pgpmime_check_signature(MimeInfo *mimeinfo);
60
61 static PrivacyDataPGP *pgpmime_new_privacydata()
62 {
63         PrivacyDataPGP *data;
64
65         data = g_new0(PrivacyDataPGP, 1);
66         data->data.system = &pgpmime_system;
67         data->done_sigtest = FALSE;
68         data->is_signed = FALSE;
69         data->sigstatus = NULL;
70         gpgme_new(&data->ctx);
71         
72         return data;
73 }
74
75 static void pgpmime_free_privacydata(PrivacyData *_data)
76 {
77         PrivacyDataPGP *data = (PrivacyDataPGP *) _data;
78         gpgme_release(data->ctx);
79         g_free(data);
80 }
81
82 static gboolean pgpmime_is_signed(MimeInfo *mimeinfo)
83 {
84         MimeInfo *parent;
85         MimeInfo *signature;
86         const gchar *protocol;
87         PrivacyDataPGP *data = NULL;
88         
89         g_return_val_if_fail(mimeinfo != NULL, FALSE);
90         if (mimeinfo->privacy != NULL) {
91                 data = (PrivacyDataPGP *) mimeinfo->privacy;
92                 if (data->done_sigtest)
93                         return data->is_signed;
94         }
95         
96         /* check parent */
97         parent = procmime_mimeinfo_parent(mimeinfo);
98         if (parent == NULL)
99                 return FALSE;
100         if ((parent->type != MIMETYPE_MULTIPART) ||
101             g_ascii_strcasecmp(parent->subtype, "signed"))
102                 return FALSE;
103         protocol = procmime_mimeinfo_get_parameter(parent, "protocol");
104         if ((protocol == NULL) || 
105             (g_ascii_strcasecmp(protocol, "application/pgp-signature")))
106                 return FALSE;
107
108         /* check if mimeinfo is the first child */
109         if (parent->node->children->data != mimeinfo)
110                 return FALSE;
111
112         /* check signature */
113         signature = parent->node->children->next != NULL ? 
114             (MimeInfo *) parent->node->children->next->data : NULL;
115         if (signature == NULL)
116                 return FALSE;
117         if ((signature->type != MIMETYPE_APPLICATION) ||
118             (g_ascii_strcasecmp(signature->subtype, "pgp-signature")))
119                 return FALSE;
120
121         if (data == NULL) {
122                 data = pgpmime_new_privacydata();
123                 mimeinfo->privacy = (PrivacyData *) data;
124         }
125         
126         data->done_sigtest = TRUE;
127         data->is_signed = TRUE;
128
129         return TRUE;
130 }
131
132 static gchar *get_canonical_content(FILE *fp, const gchar *boundary)
133 {
134         gchar *ret;
135         GString *textbuffer;
136         guint boundary_len;
137         gchar buf[BUFFSIZE];
138
139         boundary_len = strlen(boundary);
140         while (fgets(buf, sizeof(buf), fp) != NULL)
141                 if (IS_BOUNDARY(buf, boundary, boundary_len))
142                         break;
143
144         textbuffer = g_string_new("");
145         while (fgets(buf, sizeof(buf), fp) != NULL) {
146                 gchar *buf2;
147
148                 if (IS_BOUNDARY(buf, boundary, boundary_len))
149                         break;
150                 
151                 buf2 = canonicalize_str(buf);
152                 g_string_append(textbuffer, buf2);
153                 g_free(buf2);
154         }
155         g_string_truncate(textbuffer, textbuffer->len - 2);
156                 
157         ret = textbuffer->str;
158         g_string_free(textbuffer, FALSE);
159
160         return ret;
161 }
162
163 static gint pgpmime_check_signature(MimeInfo *mimeinfo)
164 {
165         PrivacyDataPGP *data;
166         MimeInfo *parent, *signature;
167         FILE *fp;
168         gchar *boundary;
169         gchar *textstr;
170         gpgme_data_t sigdata = NULL, textdata = NULL;
171         gpgme_error_t err;
172         g_return_val_if_fail(mimeinfo != NULL, -1);
173         g_return_val_if_fail(mimeinfo->privacy != NULL, -1);
174         data = (PrivacyDataPGP *) mimeinfo->privacy;
175         gpgme_new(&data->ctx);
176         
177         debug_print("Checking PGP/MIME signature\n");
178
179         err = gpgme_set_protocol(data->ctx, GPGME_PROTOCOL_OpenPGP);
180
181         if (err) {
182                 debug_print ("gpgme_set_protocol failed: %s\n",
183                    gpgme_strerror (err));
184         }
185         parent = procmime_mimeinfo_parent(mimeinfo);
186
187         fp = g_fopen(parent->data.filename, "rb");
188         g_return_val_if_fail(fp != NULL, SIGNATURE_INVALID);
189         
190         boundary = g_hash_table_lookup(parent->typeparameters, "boundary");
191         if (!boundary) {
192                 privacy_set_error(_("Signature boundary not found."));
193                 return 0;
194         }
195         textstr = get_canonical_content(fp, boundary);
196
197         err = gpgme_data_new_from_mem(&textdata, textstr, strlen(textstr), 0);
198         if (err) {
199                 debug_print ("gpgme_data_new_from_mem failed: %s\n",
200                    gpgme_strerror (err));
201         }
202         signature = (MimeInfo *) mimeinfo->node->next->data;
203         sigdata = sgpgme_data_from_mimeinfo(signature);
204
205         err = 0;
206         if (signature->encoding_type == ENC_BASE64) {
207                 err = gpgme_data_set_encoding (sigdata, GPGME_DATA_ENCODING_BASE64);
208         }
209         
210         if (err) {
211                 debug_print ("gpgme_data_set_encoding failed: %s\n",
212                         gpgme_strerror (err));
213         }
214
215         data->sigstatus =
216                 sgpgme_verify_signature (data->ctx, sigdata, textdata, NULL);
217
218         gpgme_data_release(sigdata);
219         gpgme_data_release(textdata);
220         g_free(textstr);
221         fclose(fp);
222         
223         return 0;
224 }
225
226 static SignatureStatus pgpmime_get_sig_status(MimeInfo *mimeinfo)
227 {
228         PrivacyDataPGP *data = (PrivacyDataPGP *) mimeinfo->privacy;
229         
230         g_return_val_if_fail(data != NULL, SIGNATURE_INVALID);
231
232         if (data->sigstatus == NULL && 
233             prefs_gpg_get_config()->auto_check_signatures)
234                 pgpmime_check_signature(mimeinfo);
235         
236         return sgpgme_sigstat_gpgme_to_privacy(data->ctx, data->sigstatus);
237 }
238
239 static gchar *pgpmime_get_sig_info_short(MimeInfo *mimeinfo)
240 {
241         PrivacyDataPGP *data = (PrivacyDataPGP *) mimeinfo->privacy;
242         
243         g_return_val_if_fail(data != NULL, g_strdup("Error"));
244
245         if (data->sigstatus == NULL && 
246             prefs_gpg_get_config()->auto_check_signatures)
247                 pgpmime_check_signature(mimeinfo);
248         
249         return sgpgme_sigstat_info_short(data->ctx, data->sigstatus);
250 }
251
252 static gchar *pgpmime_get_sig_info_full(MimeInfo *mimeinfo)
253 {
254         PrivacyDataPGP *data = (PrivacyDataPGP *) mimeinfo->privacy;
255         
256         g_return_val_if_fail(data != NULL, g_strdup("Error"));
257
258         if (data->sigstatus == NULL && 
259             prefs_gpg_get_config()->auto_check_signatures)
260                 pgpmime_check_signature(mimeinfo);
261         
262         return sgpgme_sigstat_info_full(data->ctx, data->sigstatus);
263 }
264
265 static gboolean pgpmime_is_encrypted(MimeInfo *mimeinfo)
266 {
267         MimeInfo *tmpinfo;
268         const gchar *tmpstr;
269         
270         if (mimeinfo->type != MIMETYPE_MULTIPART)
271                 return FALSE;
272         if (g_ascii_strcasecmp(mimeinfo->subtype, "encrypted"))
273                 return FALSE;
274         tmpstr = procmime_mimeinfo_get_parameter(mimeinfo, "protocol");
275         if ((tmpstr == NULL) || g_ascii_strcasecmp(tmpstr, "application/pgp-encrypted"))
276                 return FALSE;
277         if (g_node_n_children(mimeinfo->node) != 2)
278                 return FALSE;
279         
280         tmpinfo = (MimeInfo *) g_node_nth_child(mimeinfo->node, 0)->data;
281         if (tmpinfo->type != MIMETYPE_APPLICATION)
282                 return FALSE;
283         if (g_ascii_strcasecmp(tmpinfo->subtype, "pgp-encrypted"))
284                 return FALSE;
285         
286         tmpinfo = (MimeInfo *) g_node_nth_child(mimeinfo->node, 1)->data;
287         if (tmpinfo->type != MIMETYPE_APPLICATION)
288                 return FALSE;
289         if (g_ascii_strcasecmp(tmpinfo->subtype, "octet-stream"))
290                 return FALSE;
291         
292         return TRUE;
293 }
294
295 static MimeInfo *pgpmime_decrypt(MimeInfo *mimeinfo)
296 {
297         MimeInfo *encinfo, *decinfo, *parseinfo;
298         gpgme_data_t cipher = NULL, plain = NULL;
299         static gint id = 0;
300         FILE *dstfp;
301         gchar *fname;
302         gpgme_verify_result_t sigstat = NULL;
303         PrivacyDataPGP *data = NULL;
304         gpgme_ctx_t ctx;
305         gchar *chars;
306         size_t len;
307         gpgme_error_t err;
308
309         if ((err = gpgme_new(&ctx)) != GPG_ERR_NO_ERROR) {
310         privacy_set_error(_("Couldn't initialize GPG context, %s"), gpgme_strerror(err));
311                 return NULL;
312         }
313         
314         g_return_val_if_fail(pgpmime_is_encrypted(mimeinfo), NULL);
315         
316         encinfo = (MimeInfo *) g_node_nth_child(mimeinfo->node, 1)->data;
317
318         cipher = sgpgme_data_from_mimeinfo(encinfo);
319         plain = sgpgme_decrypt_verify(cipher, &sigstat, ctx);
320
321         gpgme_data_release(cipher);
322         if (plain == NULL) {
323                 debug_print("plain is null!\n");
324                 gpgme_release(ctx);
325                 return NULL;
326         }
327
328         fname = g_strdup_printf("%s%cplaintext.%08x",
329                 get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id);
330
331         if ((dstfp = g_fopen(fname, "wb")) == NULL) {
332                 FILE_OP_ERROR(fname, "fopen");
333                 privacy_set_error(_("Couldn't open decrypted file %s"), fname);
334                 g_free(fname);
335                 gpgme_data_release(plain);
336                 gpgme_release(ctx);
337                 debug_print("can't open!\n");
338                 return NULL;
339         }
340
341         fprintf(dstfp, "MIME-Version: 1.0\n");
342
343         chars = gpgme_data_release_and_get_mem(plain, &len);
344         if (len > 0)
345                 fwrite(chars, len, 1, dstfp);
346         fclose(dstfp);
347
348         parseinfo = procmime_scan_file(fname);
349         g_free(fname);
350         if (parseinfo == NULL) {
351                 gpgme_release(ctx);
352                 privacy_set_error(_("Couldn't parse decrypted file."));
353                 return NULL;
354         }
355         decinfo = g_node_first_child(parseinfo->node) != NULL ?
356                 g_node_first_child(parseinfo->node)->data : NULL;
357         if (decinfo == NULL) {
358                 privacy_set_error(_("Couldn't parse decrypted file parts."));
359                 gpgme_release(ctx);
360                 return NULL;
361         }
362
363         g_node_unlink(decinfo->node);
364         procmime_mimeinfo_free_all(parseinfo);
365
366         decinfo->tmp = TRUE;
367
368         if (sigstat != NULL && sigstat->signatures != NULL) {
369                 if (decinfo->privacy != NULL) {
370                         data = (PrivacyDataPGP *) decinfo->privacy;
371                 } else {
372                         data = pgpmime_new_privacydata();
373                         decinfo->privacy = (PrivacyData *) data;        
374                 }
375                 data->done_sigtest = TRUE;
376                 data->is_signed = TRUE;
377                 data->sigstatus = sigstat;
378                 if (data->ctx)
379                         gpgme_release(data->ctx);
380                 data->ctx = ctx;
381         } else
382                 gpgme_release(ctx);
383
384         return decinfo;
385 }
386
387 gboolean pgpmime_sign(MimeInfo *mimeinfo, PrefsAccount *account)
388 {
389         MimeInfo *msgcontent, *sigmultipart, *newinfo;
390         gchar *textstr, *micalg;
391         FILE *fp;
392         gchar *boundary = NULL;
393         gchar *sigcontent;
394         gpgme_ctx_t ctx;
395         gpgme_data_t gpgtext, gpgsig;
396         gpgme_error_t err;
397         size_t len;
398         struct passphrase_cb_info_s info;
399         gpgme_sign_result_t result = NULL;
400         gchar *test_msg;
401         
402         fp = my_tmpfile();
403         if (fp == NULL) {
404                 privacy_set_error(_("Couldn't create temporary file: %s"), strerror(errno));
405                 return FALSE;
406         }
407         procmime_write_mimeinfo(mimeinfo, fp);
408         rewind(fp);
409
410         /* read temporary file into memory */
411         test_msg = file_read_stream_to_str(fp);
412         fclose(fp);
413         
414         memset (&info, 0, sizeof info);
415
416         /* remove content node from message */
417         msgcontent = (MimeInfo *) mimeinfo->node->children->data;
418         g_node_unlink(msgcontent->node);
419
420         /* create temporary multipart for content */
421         sigmultipart = procmime_mimeinfo_new();
422         sigmultipart->type = MIMETYPE_MULTIPART;
423         sigmultipart->subtype = g_strdup("signed");
424         
425         do {
426                 g_free(boundary);
427                 boundary = generate_mime_boundary("Sig");
428         } while (strstr(test_msg, boundary) != NULL);
429         
430         g_free(test_msg);
431
432         g_hash_table_insert(sigmultipart->typeparameters, g_strdup("boundary"),
433                             g_strdup(boundary));
434         g_hash_table_insert(sigmultipart->typeparameters, g_strdup("protocol"),
435                             g_strdup("application/pgp-signature"));
436         g_node_append(sigmultipart->node, msgcontent->node);
437         g_node_append(mimeinfo->node, sigmultipart->node);
438
439         /* write message content to temporary file */
440         fp = my_tmpfile();
441         if (fp == NULL) {
442                 perror("my_tmpfile");
443                 privacy_set_error(_("Couldn't create temporary file: %s"), strerror(errno));
444                 return FALSE;
445         }
446         procmime_write_mimeinfo(sigmultipart, fp);
447         rewind(fp);
448
449         /* read temporary file into memory */
450         textstr = get_canonical_content(fp, boundary);
451
452         fclose(fp);
453
454         gpgme_data_new_from_mem(&gpgtext, textstr, strlen(textstr), 0);
455         gpgme_data_new(&gpgsig);
456         gpgme_new(&ctx);
457         gpgme_set_textmode(ctx, 1);
458         gpgme_set_armor(ctx, 1);
459         gpgme_signers_clear (ctx);
460
461         if (!sgpgme_setup_signers(ctx, account)) {
462                 gpgme_release(ctx);
463                 return FALSE;
464         }
465
466         if (getenv("GPG_AGENT_INFO")) {
467                 debug_print("GPG_AGENT_INFO environment defined, running without passphrase callback\n");
468         } else {
469                 info.c = ctx;
470                 gpgme_set_passphrase_cb (ctx, gpgmegtk_passphrase_cb, &info);
471         }
472
473         err = gpgme_op_sign(ctx, gpgtext, gpgsig, GPGME_SIG_MODE_DETACH);
474         if (err != GPG_ERR_NO_ERROR) {
475                 if (err == GPG_ERR_CANCELED) {
476                         /* ignore cancelled signing */
477                         privacy_reset_error();
478                         debug_print("gpgme_op_sign cancelled\n");
479                 } else {
480                         privacy_set_error(_("Data signing failed, %s"), gpgme_strerror(err));
481                         debug_print("gpgme_op_sign error : %x\n", err);
482                 }
483                 gpgme_release(ctx);
484                 return FALSE;
485         }
486         result = gpgme_op_sign_result(ctx);
487         if (result && result->signatures) {
488                 gpgme_new_signature_t sig = result->signatures;
489                 if (gpgme_get_protocol(ctx) == GPGME_PROTOCOL_OpenPGP) {
490                         micalg = g_strdup_printf("PGP-%s", gpgme_hash_algo_name(
491                                 result->signatures->hash_algo));
492                 } else {
493                         micalg = g_strdup(gpgme_hash_algo_name(
494                                 result->signatures->hash_algo));
495                 }
496                 while (sig) {
497                         debug_print("valid signature: %s\n", sig->fpr);
498                         sig = sig->next;
499                 }
500         } else if (result && result->invalid_signers) {
501                 gpgme_invalid_key_t invalid = result->invalid_signers;
502                 while (invalid) {
503                         g_warning("invalid signer: %s (%s)", invalid->fpr, 
504                                 gpgme_strerror(invalid->reason));
505                         privacy_set_error(_("Data signing failed due to invalid signer: %s"), 
506                                 gpgme_strerror(invalid->reason));
507                         invalid = invalid->next;
508                 }
509                 gpgme_release(ctx);
510                 return FALSE;
511         } else {
512                 /* can't get result (maybe no signing key?) */
513                 debug_print("gpgme_op_sign_result error\n");
514                 privacy_set_error(_("Data signing failed, no results."));
515                 gpgme_release(ctx);
516                 return FALSE;
517         }
518
519         gpgme_release(ctx);
520         sigcontent = gpgme_data_release_and_get_mem(gpgsig, &len);
521         gpgme_data_release(gpgtext);
522         g_free(textstr);
523
524         if (sigcontent == NULL || len <= 0) {
525                 g_warning("gpgme_data_release_and_get_mem failed");
526                 privacy_set_error(_("Data signing failed, no contents."));
527                 return FALSE;
528         }
529
530         /* add signature */
531         g_hash_table_insert(sigmultipart->typeparameters, g_strdup("micalg"),
532                             micalg);
533
534         newinfo = procmime_mimeinfo_new();
535         newinfo->type = MIMETYPE_APPLICATION;
536         newinfo->subtype = g_strdup("pgp-signature");
537         g_hash_table_insert(newinfo->typeparameters, g_strdup("name"),
538                              g_strdup("signature.asc"));
539         newinfo->content = MIMECONTENT_MEM;
540         newinfo->disposition = DISPOSITIONTYPE_ATTACHMENT;
541         g_hash_table_insert(newinfo->dispositionparameters, g_strdup("filename"),
542                             g_strdup("signature.asc"));
543         newinfo->data.mem = g_malloc(len + 1);
544         g_memmove(newinfo->data.mem, sigcontent, len);
545         newinfo->data.mem[len] = '\0';
546         g_node_append(sigmultipart->node, newinfo->node);
547
548         g_free(sigcontent);
549
550         return TRUE;
551 }
552 gchar *pgpmime_get_encrypt_data(GSList *recp_names)
553 {
554         return sgpgme_get_encrypt_data(recp_names, GPGME_PROTOCOL_OpenPGP);
555 }
556
557 gboolean pgpmime_encrypt(MimeInfo *mimeinfo, const gchar *encrypt_data)
558 {
559         MimeInfo *msgcontent, *encmultipart, *newinfo;
560         FILE *fp;
561         gchar *boundary, *enccontent;
562         size_t len;
563         gchar *textstr;
564         gpgme_data_t gpgtext = NULL, gpgenc = NULL;
565         gpgme_ctx_t ctx = NULL;
566         gpgme_key_t *kset = NULL;
567         gchar **fprs = g_strsplit(encrypt_data, " ", -1);
568         gint i = 0;
569         gpgme_error_t err;
570         
571         while (fprs[i] && strlen(fprs[i])) {
572                 i++;
573         }
574         
575         kset = g_malloc(sizeof(gpgme_key_t)*(i+1));
576         memset(kset, 0, sizeof(gpgme_key_t)*(i+1));
577         gpgme_new(&ctx);
578         i = 0;
579         while (fprs[i] && strlen(fprs[i])) {
580                 gpgme_key_t key;
581                 err = gpgme_get_key(ctx, fprs[i], &key, 0);
582                 if (err) {
583                         debug_print("can't add key '%s'[%d] (%s)\n", fprs[i],i, gpgme_strerror(err));
584                         privacy_set_error(_("Couldn't add GPG key %s, %s"), fprs[i], gpgme_strerror(err));
585                         return FALSE;
586                 }
587                 debug_print("found %s at %d\n", fprs[i], i);
588                 kset[i] = key;
589                 i++;
590         }
591         
592         debug_print("Encrypting message content\n");
593
594         /* remove content node from message */
595         msgcontent = (MimeInfo *) mimeinfo->node->children->data;
596         g_node_unlink(msgcontent->node);
597
598         /* create temporary multipart for content */
599         encmultipart = procmime_mimeinfo_new();
600         encmultipart->type = MIMETYPE_MULTIPART;
601         encmultipart->subtype = g_strdup("encrypted");
602         boundary = generate_mime_boundary("Encrypt");
603         g_hash_table_insert(encmultipart->typeparameters, g_strdup("boundary"),
604                             g_strdup(boundary));
605         g_hash_table_insert(encmultipart->typeparameters, g_strdup("protocol"),
606                             g_strdup("application/pgp-encrypted"));
607         g_node_append(encmultipart->node, msgcontent->node);
608
609         /* write message content to temporary file */
610         fp = my_tmpfile();
611         if (fp == NULL) {
612                 privacy_set_error(_("Couldn't create temporary file, %s"), strerror(errno));
613                 return FALSE;
614         }
615         procmime_write_mimeinfo(encmultipart, fp);
616         rewind(fp);
617
618         /* read temporary file into memory */
619         textstr = get_canonical_content(fp, boundary);
620
621         fclose(fp);
622
623         /* encrypt data */
624         gpgme_data_new_from_mem(&gpgtext, textstr, strlen(textstr), 0);
625         gpgme_data_new(&gpgenc);
626         gpgme_set_armor(ctx, 1);
627         gpgme_data_rewind(gpgtext);
628         
629         err = gpgme_op_encrypt(ctx, kset, GPGME_ENCRYPT_ALWAYS_TRUST, gpgtext, gpgenc);
630
631         gpgme_release(ctx);
632         enccontent = gpgme_data_release_and_get_mem(gpgenc, &len);
633         gpgme_data_release(gpgtext);
634         g_free(textstr);
635
636         if (enccontent == NULL || len <= 0) {
637                 g_warning("gpgme_data_release_and_get_mem failed");
638                 privacy_set_error(_("Encryption failed, %s"), gpgme_strerror(err));
639                 return FALSE;
640         }
641
642         /* create encrypted multipart */
643         g_node_unlink(msgcontent->node);
644         procmime_mimeinfo_free_all(msgcontent);
645         g_node_append(mimeinfo->node, encmultipart->node);
646
647         newinfo = procmime_mimeinfo_new();
648         newinfo->type = MIMETYPE_APPLICATION;
649         newinfo->subtype = g_strdup("pgp-encrypted");
650         newinfo->content = MIMECONTENT_MEM;
651         newinfo->data.mem = g_strdup("Version: 1\n");
652         g_node_append(encmultipart->node, newinfo->node);
653
654         newinfo = procmime_mimeinfo_new();
655         newinfo->type = MIMETYPE_APPLICATION;
656         newinfo->subtype = g_strdup("octet-stream");
657         newinfo->content = MIMECONTENT_MEM;
658         newinfo->data.mem = g_malloc(len + 1);
659         g_memmove(newinfo->data.mem, enccontent, len);
660         newinfo->data.mem[len] = '\0';
661         g_node_append(encmultipart->node, newinfo->node);
662
663         g_free(enccontent);
664
665         return TRUE;
666 }
667
668 static PrivacySystem pgpmime_system = {
669         "pgpmime",                      /* id */
670         "PGP MIME",                     /* name */
671
672         pgpmime_free_privacydata,       /* free_privacydata */
673
674         pgpmime_is_signed,              /* is_signed(MimeInfo *) */
675         pgpmime_check_signature,        /* check_signature(MimeInfo *) */
676         pgpmime_get_sig_status,         /* get_sig_status(MimeInfo *) */
677         pgpmime_get_sig_info_short,     /* get_sig_info_short(MimeInfo *) */
678         pgpmime_get_sig_info_full,      /* get_sig_info_full(MimeInfo *) */
679
680         pgpmime_is_encrypted,           /* is_encrypted(MimeInfo *) */
681         pgpmime_decrypt,                /* decrypt(MimeInfo *) */
682
683         TRUE,
684         pgpmime_sign,
685
686         TRUE,
687         pgpmime_get_encrypt_data,
688         pgpmime_encrypt,
689 };
690
691 void pgpmime_init()
692 {
693         privacy_register_system(&pgpmime_system);
694 }
695
696 void pgpmime_done()
697 {
698         privacy_unregister_system(&pgpmime_system);
699 }
700
701 struct PluginFeature *plugin_provides(void)
702 {
703         static struct PluginFeature features[] = 
704                 { {PLUGIN_PRIVACY, N_("PGP/Mime")},
705                   {PLUGIN_NOTHING, NULL}};
706         return features;
707 }
708 #endif /* USE_GPGME */