various clean-ups and optimisations by Olaf Hering
[claws.git] / src / plugins / pgpinline / pgpinline.c
blob8a9c0fab5ad59fb892aab50c5df97693be8e759a
1 /*
2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2024 the Claws Mail team and Colin Leroy
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 3 of the License, or
8 * (at your option) any later version.
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.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #include "claws-features.h"
22 #endif
24 #ifdef USE_GPGME
26 #include "defs.h"
27 #include <glib.h>
28 #include <glib/gi18n.h>
29 #include <errno.h>
30 #include <gpgme.h>
31 #include <file-utils.h>
33 #include "utils.h"
34 #include "privacy.h"
35 #include "procmime.h"
36 #include "pgpinline.h"
37 #include <plugins/pgpcore/sgpgme.h>
38 #include <plugins/pgpcore/prefs_gpg.h>
39 #include <plugins/pgpcore/passphrase.h>
40 #include <plugins/pgpcore/pgp_utils.h>
41 #include "quoted-printable.h"
42 #include "codeconv.h"
43 #include "plugin.h"
45 extern struct GPGConfig prefs_gpg;
47 typedef struct _PrivacyDataPGP PrivacyDataPGP;
49 struct _PrivacyDataPGP
51 PrivacyData data;
53 gboolean done_sigtest;
54 gboolean is_signed;
57 typedef struct _PGPInlineTaskData
59 gchar *rawtext;
60 gchar *charset;
61 } PGPInlineTaskData;
63 static PrivacySystem pgpinline_system;
65 static PrivacyDataPGP *pgpinline_new_privacydata()
67 PrivacyDataPGP *data;
69 data = g_new0(PrivacyDataPGP, 1);
70 data->data.system = &pgpinline_system;
72 return data;
75 static void pgpinline_free_privacydata(PrivacyData *data)
77 g_free(data);
80 static gboolean pgpinline_is_signed(MimeInfo *mimeinfo)
82 PrivacyDataPGP *data = NULL;
83 const gchar *sig_indicator = "-----BEGIN PGP SIGNED MESSAGE-----";
84 gchar *textdata, *sigpos;
86 cm_return_val_if_fail(mimeinfo != NULL, FALSE);
88 if (procmime_mimeinfo_parent(mimeinfo) == NULL)
89 return FALSE; /* not parent */
91 if (mimeinfo->type != MIMETYPE_TEXT &&
92 (mimeinfo->type != MIMETYPE_APPLICATION ||
93 g_ascii_strcasecmp(mimeinfo->subtype, "pgp")))
94 return FALSE;
96 /* Seal the deal. This has to be text/plain through and through. */
97 if (mimeinfo->type == MIMETYPE_APPLICATION)
99 mimeinfo->type = MIMETYPE_TEXT;
100 g_free(mimeinfo->subtype);
101 mimeinfo->subtype = g_strdup("plain");
104 if (mimeinfo->privacy != NULL) {
105 data = (PrivacyDataPGP *) mimeinfo->privacy;
106 if (data->done_sigtest)
107 return data->is_signed;
110 textdata = procmime_get_part_as_string(mimeinfo, TRUE);
111 if (!textdata)
112 return FALSE;
114 if ((sigpos = strstr(textdata, sig_indicator)) == NULL) {
115 g_free(textdata);
116 return FALSE;
119 if (!(sigpos == textdata) && !(sigpos[-1] == '\n')) {
120 g_free(textdata);
121 return FALSE;
124 g_free(textdata);
126 if (data == NULL) {
127 data = pgpinline_new_privacydata();
128 mimeinfo->privacy = (PrivacyData *) data;
130 if (data != NULL) {
131 data->done_sigtest = TRUE;
132 data->is_signed = TRUE;
135 return TRUE;
138 static void pgpinline_free_task_data(gpointer data)
140 PGPInlineTaskData *task_data = (PGPInlineTaskData *)data;
142 g_free(task_data->rawtext);
143 g_free(task_data->charset);
144 g_free(task_data);
147 static gchar *get_sig_data(gchar *rawtext, gchar *charset)
149 gchar *conv;
151 conv = conv_codeset_strdup(rawtext, CS_UTF_8, charset);
152 if (!conv)
153 conv = conv_codeset_strdup(rawtext, CS_UTF_8, conv_get_locale_charset_str_no_utf8());
155 if (!conv) {
156 g_warning("can't convert charset to anything sane");
157 conv = conv_codeset_strdup(rawtext, CS_UTF_8, CS_US_ASCII);
160 return conv;
163 static void pgpinline_check_sig_task(GTask *task,
164 gpointer source_object,
165 gpointer g_task_data,
166 GCancellable *cancellable)
168 PGPInlineTaskData *task_data = (PGPInlineTaskData *)g_task_data;
169 GQuark domain;
170 gpgme_ctx_t ctx;
171 gpgme_error_t err;
172 gpgme_data_t sigdata = NULL;
173 gpgme_data_t plain = NULL;
174 gpgme_verify_result_t gpgme_res;
175 gboolean return_err = TRUE;
176 gboolean cancelled = FALSE;
177 SigCheckTaskResult *task_result = NULL;
178 gchar *textstr;
179 char err_str[GPGERR_BUFSIZE] = "";
181 domain = g_quark_from_static_string("claws_pgpinline");
183 err = gpgme_new(&ctx);
184 if (err != GPG_ERR_NO_ERROR) {
185 gpgme_strerror_r(err, err_str, GPGERR_BUFSIZE);
186 g_warning("couldn't initialize GPG context: %s", err_str);
187 goto out;
190 gpgme_set_textmode(ctx, 1);
191 gpgme_set_armor(ctx, 1);
193 textstr = get_sig_data(task_data->rawtext, task_data->charset);
194 if (!textstr) {
195 err = GPG_ERR_GENERAL;
196 g_snprintf(err_str, GPGERR_BUFSIZE, "Couldn't convert text data to any sane charset.");
197 goto out_ctx;
200 err = gpgme_data_new_from_mem(&sigdata, textstr, strlen(textstr), 1);
201 if (err != GPG_ERR_NO_ERROR) {
202 gpgme_strerror_r(err, err_str, GPGERR_BUFSIZE);
203 g_warning("gpgme_data_new_from_mem failed: %s", err_str);
204 goto out_textstr;
207 err = gpgme_data_new(&plain);
208 if (err != GPG_ERR_NO_ERROR) {
209 gpgme_strerror_r(err, err_str, GPGERR_BUFSIZE);
210 g_warning("gpgme_data_new failed: %s", err_str);
211 goto out_sigdata;
214 if (g_task_return_error_if_cancelled(task)) {
215 debug_print("task was cancelled, aborting task:%p\n", task);
216 cancelled = TRUE;
217 goto out_sigdata;
220 err = gpgme_op_verify(ctx, sigdata, NULL, plain);
221 if (err != GPG_ERR_NO_ERROR) {
222 gpgme_strerror_r(err, err_str, GPGERR_BUFSIZE);
223 g_warning("gpgme_op_verify failed: %s\n", err_str);
224 goto out_plain;
227 if (g_task_return_error_if_cancelled(task)) {
228 debug_print("task was cancelled, aborting task:%p\n", task);
229 cancelled = TRUE;
230 goto out_sigdata;
233 gpgme_res = gpgme_op_verify_result(ctx);
234 if (gpgme_res && gpgme_res->signatures == NULL) {
235 err = GPG_ERR_SYSTEM_ERROR;
236 g_warning("no signature found");
237 g_snprintf(err_str, GPGERR_BUFSIZE, "No signature found");
238 goto out_plain;
241 task_result = g_new0(SigCheckTaskResult, 1);
242 task_result->sig_data = g_new0(SignatureData, 1);
244 task_result->sig_data->status = sgpgme_sigstat_gpgme_to_privacy(ctx, gpgme_res);
245 task_result->sig_data->info_short = sgpgme_sigstat_info_short(ctx, gpgme_res);
246 task_result->sig_data->info_full = sgpgme_sigstat_info_full(ctx, gpgme_res);
248 return_err = FALSE;
250 out_plain:
251 gpgme_data_release(plain);
252 out_sigdata:
253 gpgme_data_release(sigdata);
254 out_textstr:
255 g_free(textstr);
256 out_ctx:
257 gpgme_release(ctx);
258 out:
259 if (cancelled)
260 return;
262 if (return_err)
263 g_task_return_new_error(task, domain, err, "%s", err_str);
264 else
265 g_task_return_pointer(task, task_result, privacy_free_sig_check_task_result);
268 static gint pgpinline_check_sig_async(MimeInfo *mimeinfo,
269 GCancellable *cancellable,
270 GAsyncReadyCallback callback,
271 gpointer user_data)
273 GTask *task;
274 PGPInlineTaskData *task_data;
275 gchar *rawtext;
276 const gchar *charset;
278 if (procmime_mimeinfo_parent(mimeinfo) == NULL) {
279 g_warning("Checking signature on incorrect part");
280 return -1;
283 if (mimeinfo->type != MIMETYPE_TEXT) {
284 g_warning("Checking signature on a non-text part");
285 return -1;
288 rawtext = procmime_get_part_as_string(mimeinfo, TRUE);
289 if (rawtext == NULL) {
290 g_warning("Failed to get part as string");
291 return -1;
294 charset = procmime_mimeinfo_get_parameter(mimeinfo, "charset");
296 task_data = g_new0(PGPInlineTaskData, 1);
297 task_data->rawtext = rawtext;
298 task_data->charset = g_strdup(charset);
300 task = g_task_new(NULL, cancellable, callback, user_data);
301 mimeinfo->last_sig_check_task = task;
303 g_task_set_task_data(task, task_data, pgpinline_free_task_data);
304 debug_print("creating check sig async task:%p task_data:%p\n", task, task_data);
305 g_task_set_return_on_cancel(task, TRUE);
306 g_task_run_in_thread(task, pgpinline_check_sig_task);
307 g_object_unref(task);
309 return 0;
312 static gboolean pgpinline_is_encrypted(MimeInfo *mimeinfo)
314 const gchar *begin_indicator = "-----BEGIN PGP MESSAGE-----";
315 const gchar *end_indicator = "-----END PGP MESSAGE-----";
316 gchar *textdata;
318 cm_return_val_if_fail(mimeinfo != NULL, FALSE);
320 if (procmime_mimeinfo_parent(mimeinfo) == NULL)
321 return FALSE; /* not parent */
323 if (mimeinfo->type != MIMETYPE_TEXT &&
324 (mimeinfo->type != MIMETYPE_APPLICATION ||
325 g_ascii_strcasecmp(mimeinfo->subtype, "pgp")))
326 return FALSE;
328 /* Seal the deal. This has to be text/plain through and through. */
329 if (mimeinfo->type == MIMETYPE_APPLICATION)
331 mimeinfo->type = MIMETYPE_TEXT;
332 g_free(mimeinfo->subtype);
333 mimeinfo->subtype = g_strdup("plain");
336 textdata = procmime_get_part_as_string(mimeinfo, TRUE);
337 if (!textdata)
338 return FALSE;
340 if (!pgp_locate_armor_header(textdata, begin_indicator)) {
341 g_free(textdata);
342 return FALSE;
344 if (!pgp_locate_armor_header(textdata, end_indicator)) {
345 g_free(textdata);
346 return FALSE;
349 g_free(textdata);
351 return TRUE;
354 static MimeInfo *pgpinline_decrypt(MimeInfo *mimeinfo)
356 MimeInfo *decinfo, *parseinfo;
357 gpgme_data_t cipher, plain;
358 FILE *dstfp;
359 gchar *fname;
360 gchar *textdata = NULL;
361 static gint id = 0;
362 const gchar *src_codeset = NULL;
363 gpgme_verify_result_t sigstat = 0;
364 PrivacyDataPGP *data = NULL;
365 gpgme_ctx_t ctx;
366 gchar *chars;
367 size_t len;
368 const gchar *begin_indicator = "-----BEGIN PGP MESSAGE-----";
369 const gchar *end_indicator = "-----END PGP MESSAGE-----";
370 gchar *pos;
371 SignatureData *sig_data = NULL;
373 if (gpgme_new(&ctx) != GPG_ERR_NO_ERROR)
374 return NULL;
376 gpgme_set_textmode(ctx, 1);
377 gpgme_set_armor(ctx, 1);
379 cm_return_val_if_fail(mimeinfo != NULL, NULL);
380 cm_return_val_if_fail(pgpinline_is_encrypted(mimeinfo), NULL);
382 if (procmime_mimeinfo_parent(mimeinfo) == NULL ||
383 mimeinfo->type != MIMETYPE_TEXT) {
384 gpgme_release(ctx);
385 privacy_set_error(_("Couldn't parse mime part."));
386 return NULL;
389 textdata = procmime_get_part_as_string(mimeinfo, TRUE);
390 if (!textdata) {
391 gpgme_release(ctx);
392 privacy_set_error(_("Couldn't get text data."));
393 return NULL;
396 debug_print("decrypting '%s'\n", textdata);
397 gpgme_data_new_from_mem(&cipher, textdata, (size_t)strlen(textdata), 1);
399 plain = sgpgme_decrypt_verify(cipher, &sigstat, ctx);
401 if (sigstat != NULL && sigstat->signatures != NULL) {
402 sig_data = g_new0(SignatureData, 1);
403 sig_data->status = sgpgme_sigstat_gpgme_to_privacy(ctx, sigstat);
404 sig_data->info_short = sgpgme_sigstat_info_short(ctx, sigstat);
405 sig_data->info_full = sgpgme_sigstat_info_full(ctx, sigstat);
408 gpgme_release(ctx);
409 gpgme_data_release(cipher);
411 if (plain == NULL) {
412 g_free(textdata);
413 if (sig_data)
414 privacy_free_signature_data(sig_data);
415 return NULL;
418 fname = g_strdup_printf("%s%cplaintext.%08x",
419 get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id);
421 if ((dstfp = claws_fopen(fname, "wb")) == NULL) {
422 FILE_OP_ERROR(fname, "claws_fopen");
423 privacy_set_error(_("Couldn't open decrypted file %s"), fname);
424 if (sig_data)
425 privacy_free_signature_data(sig_data);
426 g_free(textdata);
427 g_free(fname);
428 gpgme_data_release(plain);
429 return NULL;
432 src_codeset = procmime_mimeinfo_get_parameter(mimeinfo, "charset");
433 if (src_codeset == NULL)
434 src_codeset = CS_ISO_8859_1;
436 if (fprintf(dstfp, "MIME-Version: 1.0\r\n"
437 "Content-Type: text/plain; charset=%s\r\n"
438 "Content-Transfer-Encoding: 8bit\r\n"
439 "\r\n",
440 src_codeset) < 0) {
441 FILE_OP_ERROR(fname, "fprintf");
442 privacy_set_error(_("Couldn't write to decrypted file %s"), fname);
443 goto FILE_ERROR;
446 /* Store any part before encrypted text */
447 pos = pgp_locate_armor_header(textdata, begin_indicator);
448 if (pos != NULL && (pos - textdata) > 0) {
449 if (claws_fwrite(textdata, 1, pos - textdata, dstfp) < pos - textdata) {
450 FILE_OP_ERROR(fname, "claws_fwrite");
451 privacy_set_error(_("Couldn't write to decrypted file %s"), fname);
452 goto FILE_ERROR;
456 if (claws_fwrite(_("\n--- Start of PGP/Inline encrypted data ---\n"), 1,
457 strlen(_("\n--- Start of PGP/Inline encrypted data ---\n")),
458 dstfp) < strlen(_("\n--- Start of PGP/Inline encrypted data ---\n"))) {
459 FILE_OP_ERROR(fname, "claws_fwrite");
460 privacy_set_error(_("Couldn't write to decrypted file %s"), fname);
461 goto FILE_ERROR;
463 chars = sgpgme_data_release_and_get_mem(plain, &len);
464 if (len > 0) {
465 if (claws_fwrite(chars, 1, len, dstfp) < len) {
466 FILE_OP_ERROR(fname, "claws_fwrite");
467 g_free(chars);
468 privacy_set_error(_("Couldn't write to decrypted file %s"), fname);
469 goto FILE_ERROR;
472 g_free(chars);
473 /* Store any part after encrypted text */
474 if (claws_fwrite(_("--- End of PGP/Inline encrypted data ---\n"), 1,
475 strlen(_("--- End of PGP/Inline encrypted data ---\n")),
476 dstfp) < strlen(_("--- End of PGP/Inline encrypted data ---\n"))) {
477 FILE_OP_ERROR(fname, "claws_fwrite");
478 privacy_set_error(_("Couldn't write to decrypted file %s"), fname);
479 goto FILE_ERROR;
481 if (pos != NULL) {
482 pos = pgp_locate_armor_header(pos, end_indicator);
483 if (pos != NULL && *pos != '\0') {
484 pos += strlen(end_indicator);
485 if (claws_fwrite(pos, 1, strlen(pos), dstfp) < strlen(pos)) {
486 FILE_OP_ERROR(fname, "claws_fwrite");
487 privacy_set_error(_("Couldn't write to decrypted file %s"), fname);
488 goto FILE_ERROR;
493 g_free(textdata);
495 if (claws_safe_fclose(dstfp) == EOF) {
496 FILE_OP_ERROR(fname, "claws_fclose");
497 privacy_set_error(_("Couldn't close decrypted file %s"), fname);
498 g_free(fname);
499 gpgme_data_release(plain);
500 if (sig_data)
501 privacy_free_signature_data(sig_data);
502 return NULL;
505 parseinfo = procmime_scan_file(fname);
506 g_free(fname);
508 if (parseinfo == NULL) {
509 privacy_set_error(_("Couldn't scan decrypted file."));
510 if (sig_data)
511 privacy_free_signature_data(sig_data);
512 return NULL;
514 decinfo = g_node_first_child(parseinfo->node) != NULL ?
515 g_node_first_child(parseinfo->node)->data : NULL;
517 if (decinfo == NULL) {
518 privacy_set_error(_("Couldn't scan decrypted file parts."));
519 if (sig_data)
520 privacy_free_signature_data(sig_data);
521 return NULL;
524 g_node_unlink(decinfo->node);
525 procmime_mimeinfo_free_all(&parseinfo);
527 decinfo->tmp = TRUE;
529 if (sig_data != NULL) {
530 if (decinfo->privacy != NULL) {
531 data = (PrivacyDataPGP *) decinfo->privacy;
532 } else {
533 data = pgpinline_new_privacydata();
534 decinfo->privacy = (PrivacyData *) data;
536 if (data != NULL) {
537 data->done_sigtest = TRUE;
538 data->is_signed = TRUE;
539 decinfo->sig_data = sig_data;
543 return decinfo;
545 FILE_ERROR:
546 if (sig_data)
547 privacy_free_signature_data(sig_data);
548 g_free(textdata);
549 claws_fclose(dstfp);
550 g_free(fname);
551 gpgme_data_release(plain);
552 return NULL;
555 static gboolean pgpinline_sign(MimeInfo *mimeinfo, PrefsAccount *account, const gchar *from_addr)
557 MimeInfo *msgcontent;
558 gchar *textstr, *tmp;
559 FILE *fp;
560 gchar *sigcontent;
561 gpgme_ctx_t ctx;
562 gpgme_data_t gpgtext, gpgsig;
563 size_t len;
564 gpgme_error_t err;
565 struct passphrase_cb_info_s info;
566 gpgme_sign_result_t result = NULL;
568 memset (&info, 0, sizeof info);
570 /* get content node from message */
571 msgcontent = (MimeInfo *) mimeinfo->node->children->data;
572 if (msgcontent->type == MIMETYPE_MULTIPART) {
573 if (!msgcontent->node->children) {
574 debug_print("msgcontent->node->children NULL, bailing\n");
575 privacy_set_error(_("Malformed message"));
576 return FALSE;
578 msgcontent = (MimeInfo *) msgcontent->node->children->data;
580 /* get rid of quoted-printable or anything */
581 procmime_decode_content(msgcontent);
583 fp = my_tmpfile();
584 if (fp == NULL) {
585 perror("my_tmpfile");
586 privacy_set_error(_("Couldn't create temporary file, %s"), g_strerror(errno));
587 return FALSE;
589 procmime_write_mimeinfo(msgcontent, fp);
590 rewind(fp);
592 /* read temporary file into memory */
593 textstr = file_read_stream_to_str_no_recode(fp);
595 claws_fclose(fp);
597 gpgme_data_new_from_mem(&gpgtext, textstr, (size_t)strlen(textstr), 0);
598 gpgme_data_new(&gpgsig);
599 if ((err = gpgme_new(&ctx)) != GPG_ERR_NO_ERROR) {
600 debug_print("Couldn't initialize GPG context, %s\n", gpgme_strerror(err));
601 privacy_set_error(_("Couldn't initialize GPG context, %s"), gpgme_strerror(err));
602 return FALSE;
604 gpgme_set_textmode(ctx, 1);
605 gpgme_set_armor(ctx, 1);
607 if (!sgpgme_setup_signers(ctx, account, from_addr)) {
608 gpgme_release(ctx);
609 return FALSE;
612 prefs_gpg_enable_agent(prefs_gpg_get_config()->use_gpg_agent);
613 if (!g_getenv("GPG_AGENT_INFO") || !prefs_gpg_get_config()->use_gpg_agent) {
614 info.c = ctx;
615 gpgme_set_passphrase_cb (ctx, gpgmegtk_passphrase_cb, &info);
618 err = gpgme_op_sign(ctx, gpgtext, gpgsig, GPGME_SIG_MODE_CLEAR);
619 if (err != GPG_ERR_NO_ERROR) {
620 if (err == GPG_ERR_CANCELED) {
621 /* ignore cancelled signing */
622 privacy_reset_error();
623 debug_print("gpgme_op_sign cancelled\n");
624 } else {
625 privacy_set_error(_("Data signing failed, %s"), gpgme_strerror(err));
626 debug_print("gpgme_op_sign error : %x\n", err);
628 gpgme_release(ctx);
629 return FALSE;
631 result = gpgme_op_sign_result(ctx);
632 if (result && result->signatures) {
633 gpgme_new_signature_t sig = result->signatures;
634 while (sig) {
635 debug_print("valid signature: %s\n", sig->fpr);
636 sig = sig->next;
638 } else if (result && result->invalid_signers) {
639 gpgme_invalid_key_t invalid = result->invalid_signers;
640 while (invalid) {
641 g_warning("invalid signer: %s (%s)", invalid->fpr,
642 gpgme_strerror(invalid->reason));
643 privacy_set_error(_("Data signing failed due to invalid signer: %s"),
644 gpgme_strerror(invalid->reason));
645 invalid = invalid->next;
647 gpgme_release(ctx);
648 return FALSE;
649 } else {
650 /* can't get result (maybe no signing key?) */
651 debug_print("gpgme_op_sign_result error\n");
652 privacy_set_error(_("Data signing failed, no results."));
653 gpgme_release(ctx);
654 return FALSE;
658 sigcontent = sgpgme_data_release_and_get_mem(gpgsig, &len);
660 if (sigcontent == NULL || len <= 0) {
661 g_warning("sgpgme_data_release_and_get_mem failed");
662 privacy_set_error(_("Data signing failed, no contents."));
663 gpgme_data_release(gpgtext);
664 g_free(textstr);
665 g_free(sigcontent);
666 gpgme_release(ctx);
667 return FALSE;
670 tmp = g_malloc(len+1);
671 memmove(tmp, sigcontent, len+1);
672 tmp[len] = '\0';
673 gpgme_data_release(gpgtext);
674 g_free(textstr);
675 g_free(sigcontent);
677 if (msgcontent->content == MIMECONTENT_FILE &&
678 msgcontent->data.filename != NULL) {
679 if (msgcontent->tmp == TRUE)
680 claws_unlink(msgcontent->data.filename);
681 g_free(msgcontent->data.filename);
683 msgcontent->data.mem = g_strdup(tmp);
684 msgcontent->content = MIMECONTENT_MEM;
685 g_free(tmp);
687 /* avoid all sorts of clear-signing problems with non ascii
688 * chars
690 procmime_encode_content(msgcontent, ENC_BASE64);
691 gpgme_release(ctx);
693 return TRUE;
696 static gchar *pgpinline_get_encrypt_data(GSList *recp_names)
698 return sgpgme_get_encrypt_data(recp_names, GPGME_PROTOCOL_OpenPGP);
701 static const gchar *pgpinline_get_encrypt_warning(void)
703 if (prefs_gpg_should_skip_encryption_warning(pgpinline_system.id))
704 return NULL;
705 else
706 return _("Please note that attachments are not encrypted by "
707 "the PGP/Inline system, nor are email headers, like Subject.");
710 static void pgpinline_inhibit_encrypt_warning(gboolean inhibit)
712 if (inhibit)
713 prefs_gpg_add_skip_encryption_warning(pgpinline_system.id);
714 else
715 prefs_gpg_remove_skip_encryption_warning(pgpinline_system.id);
718 static gboolean pgpinline_encrypt(MimeInfo *mimeinfo, const gchar *encrypt_data)
720 MimeInfo *msgcontent;
721 FILE *fp;
722 gchar *enccontent;
723 size_t len;
724 gchar *textstr, *tmp;
725 gpgme_data_t gpgtext, gpgenc;
726 gpgme_ctx_t ctx;
727 gpgme_key_t *kset = NULL;
728 gchar **fprs = g_strsplit(encrypt_data, " ", -1);
729 gpgme_error_t err;
730 gint i = 0;
732 while (fprs[i] && strlen(fprs[i])) {
733 i++;
736 kset = g_malloc(sizeof(gpgme_key_t)*(i+1));
737 memset(kset, 0, sizeof(gpgme_key_t)*(i+1));
738 if ((err = gpgme_new(&ctx)) != GPG_ERR_NO_ERROR) {
739 debug_print("Couldn't initialize GPG context, %s\n", gpgme_strerror(err));
740 privacy_set_error(_("Couldn't initialize GPG context, %s"), gpgme_strerror(err));
741 g_free(kset);
742 g_free(fprs);
743 return FALSE;
745 i = 0;
746 while (fprs[i] && strlen(fprs[i])) {
747 gpgme_key_t key;
748 err = gpgme_get_key(ctx, fprs[i], &key, 0);
749 if (err) {
750 debug_print("can't add key '%s'[%d] (%s)\n", fprs[i],i, gpgme_strerror(err));
751 privacy_set_error(_("Couldn't add GPG key %s, %s"), fprs[i], gpgme_strerror(err));
752 for (gint x = 0; x < i; x++)
753 gpgme_key_unref(kset[x]);
754 g_free(kset);
755 g_free(fprs);
756 return FALSE;
758 debug_print("found %s at %d\n", fprs[i], i);
759 kset[i] = key;
760 i++;
763 debug_print("Encrypting message content\n");
765 /* get content node from message */
766 msgcontent = (MimeInfo *) mimeinfo->node->children->data;
767 if (msgcontent->type == MIMETYPE_MULTIPART) {
768 if (!msgcontent->node->children) {
769 debug_print("msgcontent->node->children NULL, bailing\n");
770 privacy_set_error(_("Malformed message"));
771 for (gint x = 0; x < i; x++)
772 gpgme_key_unref(kset[x]);
773 g_free(kset);
774 g_free(fprs);
775 return FALSE;
777 msgcontent = (MimeInfo *) msgcontent->node->children->data;
779 /* get rid of quoted-printable or anything */
780 procmime_decode_content(msgcontent);
782 fp = my_tmpfile();
783 if (fp == NULL) {
784 privacy_set_error(_("Couldn't create temporary file, %s"), g_strerror(errno));
785 perror("my_tmpfile");
786 for (gint x = 0; x < i; x++)
787 gpgme_key_unref(kset[x]);
788 g_free(kset);
789 g_free(fprs);
790 return FALSE;
792 procmime_write_mimeinfo(msgcontent, fp);
793 rewind(fp);
795 /* read temporary file into memory */
796 textstr = file_read_stream_to_str_no_recode(fp);
798 claws_fclose(fp);
800 /* encrypt data */
801 gpgme_data_new_from_mem(&gpgtext, textstr, (size_t)strlen(textstr), 0);
802 gpgme_data_new(&gpgenc);
803 if ((err = gpgme_new(&ctx)) != GPG_ERR_NO_ERROR) {
804 debug_print("Couldn't initialize GPG context, %s\n", gpgme_strerror(err));
805 privacy_set_error(_("Couldn't initialize GPG context, %s"), gpgme_strerror(err));
806 for (gint x = 0; x < i; x++)
807 gpgme_key_unref(kset[x]);
808 g_free(kset);
809 g_free(fprs);
810 return FALSE;
812 gpgme_set_armor(ctx, 1);
814 err = gpgme_op_encrypt(ctx, kset, GPGME_ENCRYPT_ALWAYS_TRUST, gpgtext, gpgenc);
816 enccontent = sgpgme_data_release_and_get_mem(gpgenc, &len);
817 for (gint x = 0; x < i; x++)
818 gpgme_key_unref(kset[x]);
819 g_free(kset);
821 if (enccontent == NULL || len <= 0) {
822 g_warning("sgpgme_data_release_and_get_mem failed");
823 privacy_set_error(_("Encryption failed, %s"), gpgme_strerror(err));
824 gpgme_data_release(gpgtext);
825 g_free(textstr);
826 gpgme_release(ctx);
827 g_free(enccontent);
828 g_free(fprs);
829 return FALSE;
832 tmp = g_malloc(len+1);
833 memmove(tmp, enccontent, len+1);
834 tmp[len] = '\0';
835 g_free(enccontent);
837 gpgme_data_release(gpgtext);
838 g_free(textstr);
840 if (msgcontent->content == MIMECONTENT_FILE &&
841 msgcontent->data.filename != NULL) {
842 if (msgcontent->tmp == TRUE)
843 claws_unlink(msgcontent->data.filename);
844 g_free(msgcontent->data.filename);
846 msgcontent->data.mem = g_strdup(tmp);
847 msgcontent->content = MIMECONTENT_MEM;
848 g_free(tmp);
849 gpgme_release(ctx);
851 g_free(fprs);
853 return TRUE;
856 static PrivacySystem pgpinline_system = {
857 "pgpinline", /* id */
858 "PGP Inline", /* name */
860 pgpinline_free_privacydata, /* free_privacydata */
862 pgpinline_is_signed, /* is_signed(MimeInfo *) */
863 pgpinline_check_sig_async,
865 pgpinline_is_encrypted, /* is_encrypted(MimeInfo *) */
866 pgpinline_decrypt, /* decrypt(MimeInfo *) */
868 TRUE,
869 pgpinline_sign,
871 TRUE,
872 pgpinline_get_encrypt_data,
873 pgpinline_encrypt,
874 pgpinline_get_encrypt_warning,
875 pgpinline_inhibit_encrypt_warning,
876 prefs_gpg_auto_check_signatures,
879 void pgpinline_init()
881 privacy_register_system(&pgpinline_system);
884 void pgpinline_done()
886 privacy_unregister_system(&pgpinline_system);
889 struct PluginFeature *plugin_provides(void)
891 static struct PluginFeature features[] =
892 { {PLUGIN_PRIVACY, N_("PGP/Inline")},
893 {PLUGIN_NOTHING, NULL}};
894 return features;
896 #endif /* USE_GPGME */