2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2021 the Claws Mail team
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/>.
21 #include "claws-features.h"
28 #include <glib/gi18n.h>
39 #include <plugins/pgpcore/sgpgme.h>
40 #include <plugins/pgpcore/prefs_gpg.h>
41 #include <plugins/pgpcore/passphrase.h>
42 #include <plugins/pgpcore/pgp_utils.h>
44 #include "prefs_common.h"
45 #include "file-utils.h"
47 typedef struct _PrivacyDataPGP PrivacyDataPGP
;
49 struct _PrivacyDataPGP
53 gboolean done_sigtest
;
57 static PrivacySystem pgpmime_system
;
59 static PrivacyDataPGP
*pgpmime_new_privacydata()
63 data
= g_new0(PrivacyDataPGP
, 1);
64 data
->data
.system
= &pgpmime_system
;
69 static void pgpmime_free_privacydata(PrivacyData
*data
)
74 static gboolean
pgpmime_is_signed(MimeInfo
*mimeinfo
)
78 const gchar
*protocol
;
79 PrivacyDataPGP
*data
= NULL
;
81 cm_return_val_if_fail(mimeinfo
!= NULL
, FALSE
);
82 if (mimeinfo
->privacy
!= NULL
) {
83 data
= (PrivacyDataPGP
*) mimeinfo
->privacy
;
84 if (data
->done_sigtest
)
85 return data
->is_signed
;
89 parent
= procmime_mimeinfo_parent(mimeinfo
);
92 if ((parent
->type
!= MIMETYPE_MULTIPART
) ||
93 g_ascii_strcasecmp(parent
->subtype
, "signed"))
95 protocol
= procmime_mimeinfo_get_parameter(parent
, "protocol");
96 if ((protocol
== NULL
) ||
97 (g_ascii_strcasecmp(protocol
, "application/pgp-signature")))
100 /* check if mimeinfo is the first child */
101 if (parent
->node
->children
->data
!= mimeinfo
)
104 /* check signature */
105 signature
= parent
->node
->children
->next
!= NULL
?
106 (MimeInfo
*) parent
->node
->children
->next
->data
: NULL
;
107 if (signature
== NULL
)
109 if ((signature
->type
!= MIMETYPE_APPLICATION
) ||
110 (g_ascii_strcasecmp(signature
->subtype
, "pgp-signature")))
114 data
= pgpmime_new_privacydata();
115 mimeinfo
->privacy
= (PrivacyData
*) data
;
118 data
->done_sigtest
= TRUE
;
119 data
->is_signed
= TRUE
;
125 static gchar
*get_canonical_content(FILE *fp
, const gchar
*boundary
)
131 boundary_len
= strlen(boundary
);
132 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
)
133 if (IS_BOUNDARY(buf
, boundary
, boundary_len
))
136 textbuffer
= g_string_new("");
137 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
140 if (IS_BOUNDARY(buf
, boundary
, boundary_len
))
143 buf2
= canonicalize_str(buf
);
144 g_string_append(textbuffer
, buf2
);
147 g_string_truncate(textbuffer
, textbuffer
->len
- 2);
149 return g_string_free(textbuffer
, FALSE
);
152 static gint
pgpmime_check_sig_async(MimeInfo
*mimeinfo
,
153 GCancellable
*cancellable
,
154 GAsyncReadyCallback callback
,
157 return cm_check_detached_sig_async(mimeinfo
,
161 GPGME_PROTOCOL_OpenPGP
,
162 get_canonical_content
);
165 static gboolean
pgpmime_is_encrypted(MimeInfo
*mimeinfo
)
169 const gchar
*begin_indicator
= "-----BEGIN PGP MESSAGE-----";
170 const gchar
*end_indicator
= "-----END PGP MESSAGE-----";
173 if (mimeinfo
->type
!= MIMETYPE_MULTIPART
)
175 if (g_ascii_strcasecmp(mimeinfo
->subtype
, "encrypted"))
177 tmpstr
= procmime_mimeinfo_get_parameter(mimeinfo
, "protocol");
178 if ((tmpstr
== NULL
) || g_ascii_strcasecmp(tmpstr
, "application/pgp-encrypted"))
180 if (g_node_n_children(mimeinfo
->node
) != 2)
183 tmpinfo
= (MimeInfo
*) g_node_nth_child(mimeinfo
->node
, 0)->data
;
184 if (tmpinfo
->type
!= MIMETYPE_APPLICATION
)
186 if (g_ascii_strcasecmp(tmpinfo
->subtype
, "pgp-encrypted"))
189 tmpinfo
= (MimeInfo
*) g_node_nth_child(mimeinfo
->node
, 1)->data
;
190 if (tmpinfo
->type
!= MIMETYPE_APPLICATION
)
192 if (g_ascii_strcasecmp(tmpinfo
->subtype
, "octet-stream"))
195 textdata
= procmime_get_part_as_string(tmpinfo
, TRUE
);
199 if (!pgp_locate_armor_header(textdata
, begin_indicator
)) {
203 if (!pgp_locate_armor_header(textdata
, end_indicator
)) {
213 static MimeInfo
*pgpmime_decrypt(MimeInfo
*mimeinfo
)
215 MimeInfo
*encinfo
, *decinfo
, *parseinfo
;
216 gpgme_data_t cipher
= NULL
, plain
= NULL
;
220 gpgme_verify_result_t sigstat
= NULL
;
221 PrivacyDataPGP
*data
= NULL
;
226 SignatureData
*sig_data
= NULL
;
228 if ((err
= gpgme_new(&ctx
)) != GPG_ERR_NO_ERROR
) {
229 debug_print(("Couldn't initialize GPG context, %s\n"), gpgme_strerror(err
));
230 privacy_set_error(_("Couldn't initialize GPG context, %s"), gpgme_strerror(err
));
234 cm_return_val_if_fail(pgpmime_is_encrypted(mimeinfo
), NULL
);
236 encinfo
= (MimeInfo
*) g_node_nth_child(mimeinfo
->node
, 1)->data
;
238 cipher
= sgpgme_data_from_mimeinfo(encinfo
);
239 plain
= sgpgme_decrypt_verify(cipher
, &sigstat
, ctx
);
241 if (sigstat
!= NULL
&& sigstat
->signatures
!= NULL
) {
242 sig_data
= g_new0(SignatureData
, 1);
243 sig_data
->status
= sgpgme_sigstat_gpgme_to_privacy(ctx
, sigstat
);
244 sig_data
->info_short
= sgpgme_sigstat_info_short(ctx
, sigstat
);
245 sig_data
->info_full
= sgpgme_sigstat_info_full(ctx
, sigstat
);
249 gpgme_data_release(cipher
);
251 debug_print("plain is null!\n");
253 privacy_free_signature_data(sig_data
);
257 fname
= g_strdup_printf("%s%cplaintext.%08x",
258 get_mime_tmp_dir(), G_DIR_SEPARATOR
, ++id
);
260 if ((dstfp
= claws_fopen(fname
, "wb")) == NULL
) {
261 FILE_OP_ERROR(fname
, "claws_fopen");
262 privacy_set_error(_("Couldn't open decrypted file %s"), fname
);
264 privacy_free_signature_data(sig_data
);
266 gpgme_data_release(plain
);
267 debug_print("can't open!\n");
271 if (fprintf(dstfp
, "MIME-Version: 1.0\n") < 0) {
272 FILE_OP_ERROR(fname
, "fprintf");
274 privacy_set_error(_("Couldn't write to decrypted file %s"), fname
);
276 privacy_free_signature_data(sig_data
);
278 gpgme_data_release(plain
);
279 debug_print("can't open!\n");
283 chars
= sgpgme_data_release_and_get_mem(plain
, &len
);
285 if (claws_fwrite(chars
, 1, len
, dstfp
) < len
) {
286 FILE_OP_ERROR(fname
, "claws_fwrite");
289 privacy_set_error(_("Couldn't write to decrypted file %s"), fname
);
291 privacy_free_signature_data(sig_data
);
293 gpgme_data_release(plain
);
294 debug_print("can't open!\n");
300 if (claws_safe_fclose(dstfp
) == EOF
) {
301 FILE_OP_ERROR(fname
, "claws_fclose");
302 privacy_set_error(_("Couldn't close decrypted file %s"), fname
);
304 privacy_free_signature_data(sig_data
);
306 gpgme_data_release(plain
);
307 debug_print("can't open!\n");
311 parseinfo
= procmime_scan_file(fname
);
313 if (parseinfo
== NULL
) {
314 privacy_set_error(_("Couldn't parse decrypted file."));
316 privacy_free_signature_data(sig_data
);
319 decinfo
= g_node_first_child(parseinfo
->node
) != NULL
?
320 g_node_first_child(parseinfo
->node
)->data
: NULL
;
321 if (decinfo
== NULL
) {
322 privacy_set_error(_("Couldn't parse decrypted file parts."));
324 privacy_free_signature_data(sig_data
);
328 g_node_unlink(decinfo
->node
);
329 procmime_mimeinfo_free_all(&parseinfo
);
333 if (sig_data
!= NULL
) {
334 if (decinfo
->privacy
!= NULL
) {
335 data
= (PrivacyDataPGP
*) decinfo
->privacy
;
337 data
= pgpmime_new_privacydata();
338 decinfo
->privacy
= (PrivacyData
*) data
;
342 data
->done_sigtest
= TRUE
;
343 data
->is_signed
= TRUE
;
344 decinfo
->sig_data
= sig_data
;
351 gboolean
pgpmime_sign(MimeInfo
*mimeinfo
, PrefsAccount
*account
, const gchar
*from_addr
)
353 MimeInfo
*msgcontent
, *sigmultipart
, *newinfo
;
354 gchar
*textstr
, *micalg
= NULL
;
356 gchar
*boundary
= NULL
;
359 gpgme_data_t gpgtext
, gpgsig
;
362 struct passphrase_cb_info_s info
;
363 gpgme_sign_result_t result
= NULL
;
368 perror("my_tmpfile");
369 privacy_set_error(_("Couldn't create temporary file: %s"), g_strerror(errno
));
372 procmime_write_mimeinfo(mimeinfo
, fp
);
375 /* read temporary file into memory */
376 test_msg
= file_read_stream_to_str(fp
);
379 memset (&info
, 0, sizeof info
);
381 /* remove content node from message */
382 msgcontent
= (MimeInfo
*) mimeinfo
->node
->children
->data
;
383 g_node_unlink(msgcontent
->node
);
385 /* create temporary multipart for content */
386 sigmultipart
= procmime_mimeinfo_new();
387 sigmultipart
->type
= MIMETYPE_MULTIPART
;
388 sigmultipart
->subtype
= g_strdup("signed");
392 boundary
= generate_mime_boundary("Sig");
393 } while (strstr(test_msg
, boundary
) != NULL
);
397 g_hash_table_insert(sigmultipart
->typeparameters
, g_strdup("boundary"),
399 g_hash_table_insert(sigmultipart
->typeparameters
, g_strdup("protocol"),
400 g_strdup("application/pgp-signature"));
401 g_node_append(sigmultipart
->node
, msgcontent
->node
);
402 g_node_append(mimeinfo
->node
, sigmultipart
->node
);
404 /* write message content to temporary file */
407 perror("my_tmpfile");
408 privacy_set_error(_("Couldn't create temporary file: %s"), g_strerror(errno
));
412 procmime_write_mimeinfo(sigmultipart
, fp
);
415 /* read temporary file into memory */
416 textstr
= get_canonical_content(fp
, boundary
);
421 gpgme_data_new_from_mem(&gpgtext
, textstr
, (size_t)strlen(textstr
), 0);
422 gpgme_data_new(&gpgsig
);
423 if ((err
= gpgme_new(&ctx
)) != GPG_ERR_NO_ERROR
) {
424 debug_print(("Couldn't initialize GPG context, %s\n"), gpgme_strerror(err
));
425 privacy_set_error(_("Couldn't initialize GPG context, %s"), gpgme_strerror(err
));
428 gpgme_set_textmode(ctx
, 1);
429 gpgme_set_armor(ctx
, 1);
430 gpgme_signers_clear (ctx
);
432 if (!sgpgme_setup_signers(ctx
, account
, from_addr
)) {
437 prefs_gpg_enable_agent(prefs_gpg_get_config()->use_gpg_agent
);
438 if (g_getenv("GPG_AGENT_INFO") && prefs_gpg_get_config()->use_gpg_agent
) {
439 debug_print("GPG_AGENT_INFO environment defined, running without passphrase callback\n");
442 gpgme_set_passphrase_cb (ctx
, gpgmegtk_passphrase_cb
, &info
);
445 err
= gpgme_op_sign(ctx
, gpgtext
, gpgsig
, GPGME_SIG_MODE_DETACH
);
446 if (err
!= GPG_ERR_NO_ERROR
) {
447 if (err
== GPG_ERR_CANCELED
) {
448 /* ignore cancelled signing */
449 privacy_reset_error();
450 debug_print("gpgme_op_sign cancelled\n");
452 privacy_set_error(_("Data signing failed, %s"), gpgme_strerror(err
));
453 debug_print("gpgme_op_sign error : %x\n", err
);
458 result
= gpgme_op_sign_result(ctx
);
459 if (result
&& result
->signatures
) {
460 gpgme_new_signature_t sig
= result
->signatures
;
461 if (gpgme_get_protocol(ctx
) == GPGME_PROTOCOL_OpenPGP
) {
462 gchar
*down_algo
= g_ascii_strdown(gpgme_hash_algo_name(
463 result
->signatures
->hash_algo
), -1);
464 micalg
= g_strdup_printf("pgp-%s", down_algo
);
467 micalg
= g_strdup(gpgme_hash_algo_name(
468 result
->signatures
->hash_algo
));
471 debug_print("valid signature: %s\n", sig
->fpr
);
474 } else if (result
&& result
->invalid_signers
) {
475 gpgme_invalid_key_t invalid
= result
->invalid_signers
;
477 g_warning("invalid signer: %s (%s)", invalid
->fpr
,
478 gpgme_strerror(invalid
->reason
));
479 privacy_set_error(_("Data signing failed due to invalid signer: %s"),
480 gpgme_strerror(invalid
->reason
));
481 invalid
= invalid
->next
;
486 /* can't get result (maybe no signing key?) */
487 debug_print("gpgme_op_sign_result error\n");
488 privacy_set_error(_("Data signing failed, no results."));
493 sigcontent
= sgpgme_data_release_and_get_mem(gpgsig
, &len
);
494 gpgme_data_release(gpgtext
);
497 if (sigcontent
== NULL
|| len
<= 0) {
498 g_warning("sgpgme_data_release_and_get_mem failed");
499 privacy_set_error(_("Data signing failed, no contents."));
507 g_hash_table_insert(sigmultipart
->typeparameters
, g_strdup("micalg"),
510 newinfo
= procmime_mimeinfo_new();
511 newinfo
->type
= MIMETYPE_APPLICATION
;
512 newinfo
->subtype
= g_strdup("pgp-signature");
513 newinfo
->description
= g_strdup(_("OpenPGP digital signature"));
514 newinfo
->content
= MIMECONTENT_MEM
;
515 newinfo
->data
.mem
= g_malloc(len
+ 1);
516 memmove(newinfo
->data
.mem
, sigcontent
, len
);
517 newinfo
->data
.mem
[len
] = '\0';
519 g_node_append(sigmultipart
->node
, newinfo
->node
);
526 gchar
*pgpmime_get_encrypt_data(GSList
*recp_names
)
528 return sgpgme_get_encrypt_data(recp_names
, GPGME_PROTOCOL_OpenPGP
);
531 static const gchar
*pgpmime_get_encrypt_warning(void)
533 if (prefs_gpg_should_skip_encryption_warning(pgpmime_system
.id
))
536 return _("Please note that email headers, like Subject, "
537 "are not encrypted by the PGP/Mime system.");
540 static void pgpmime_inhibit_encrypt_warning(gboolean inhibit
)
543 prefs_gpg_add_skip_encryption_warning(pgpmime_system
.id
);
545 prefs_gpg_remove_skip_encryption_warning(pgpmime_system
.id
);
548 gboolean
pgpmime_encrypt(MimeInfo
*mimeinfo
, const gchar
*encrypt_data
)
550 MimeInfo
*msgcontent
, *encmultipart
, *newinfo
;
552 gchar
*boundary
, *enccontent
;
555 gpgme_data_t gpgtext
= NULL
, gpgenc
= NULL
;
556 gpgme_ctx_t ctx
= NULL
;
557 gpgme_key_t
*kset
= NULL
;
558 gchar
**fprs
= g_strsplit(encrypt_data
, " ", -1);
562 while (fprs
[i
] && strlen(fprs
[i
])) {
566 kset
= g_malloc0(sizeof(gpgme_key_t
)*(i
+1));
567 if ((err
= gpgme_new(&ctx
)) != GPG_ERR_NO_ERROR
) {
568 debug_print(("Couldn't initialize GPG context, %s\n"), gpgme_strerror(err
));
569 privacy_set_error(_("Couldn't initialize GPG context, %s"), gpgme_strerror(err
));
575 while (fprs
[i
] && strlen(fprs
[i
])) {
577 err
= gpgme_get_key(ctx
, fprs
[i
], &key
, 0);
579 debug_print("can't add key '%s'[%d] (%s)\n", fprs
[i
],i
, gpgme_strerror(err
));
580 privacy_set_error(_("Couldn't add GPG key %s, %s"), fprs
[i
], gpgme_strerror(err
));
581 for (gint x
= 0; x
< i
; x
++)
582 gpgme_key_unref(kset
[x
]);
588 debug_print("found %s at %d\n", fprs
[i
], i
);
593 debug_print("Encrypting message content\n");
595 /* remove content node from message */
596 msgcontent
= (MimeInfo
*) mimeinfo
->node
->children
->data
;
597 g_node_unlink(msgcontent
->node
);
599 /* create temporary multipart for content */
600 encmultipart
= procmime_mimeinfo_new();
601 encmultipart
->type
= MIMETYPE_MULTIPART
;
602 encmultipart
->subtype
= g_strdup("encrypted");
603 boundary
= generate_mime_boundary("Encrypt");
604 g_hash_table_insert(encmultipart
->typeparameters
, g_strdup("boundary"),
606 g_hash_table_insert(encmultipart
->typeparameters
, g_strdup("protocol"),
607 g_strdup("application/pgp-encrypted"));
608 g_node_append(encmultipart
->node
, msgcontent
->node
);
610 /* write message content to temporary file */
613 perror("my_tmpfile");
614 privacy_set_error(_("Couldn't create temporary file, %s"), g_strerror(errno
));
615 for (gint x
= 0; x
< i
; x
++)
616 gpgme_key_unref(kset
[x
]);
623 procmime_write_mimeinfo(encmultipart
, fp
);
626 /* read temporary file into memory */
627 textstr
= get_canonical_content(fp
, boundary
);
633 gpgme_data_new_from_mem(&gpgtext
, textstr
, (size_t)strlen(textstr
), 0);
634 gpgme_data_new(&gpgenc
);
635 gpgme_set_armor(ctx
, 1);
636 cm_gpgme_data_rewind(gpgtext
);
638 err
= gpgme_op_encrypt(ctx
, kset
, GPGME_ENCRYPT_ALWAYS_TRUST
, gpgtext
, gpgenc
);
640 enccontent
= sgpgme_data_release_and_get_mem(gpgenc
, &len
);
641 gpgme_data_release(gpgtext
);
643 for (gint x
= 0; x
< i
; x
++)
644 gpgme_key_unref(kset
[x
]);
647 if (enccontent
== NULL
|| len
<= 0) {
648 g_warning("sgpgme_data_release_and_get_mem failed");
649 privacy_set_error(_("Encryption failed, %s"), gpgme_strerror(err
));
656 /* create encrypted multipart */
657 g_node_unlink(msgcontent
->node
);
658 procmime_mimeinfo_free_all(&msgcontent
);
659 g_node_append(mimeinfo
->node
, encmultipart
->node
);
661 newinfo
= procmime_mimeinfo_new();
662 newinfo
->type
= MIMETYPE_APPLICATION
;
663 newinfo
->subtype
= g_strdup("pgp-encrypted");
664 newinfo
->content
= MIMECONTENT_MEM
;
665 newinfo
->data
.mem
= g_strdup("Version: 1\n");
667 g_node_append(encmultipart
->node
, newinfo
->node
);
669 newinfo
= procmime_mimeinfo_new();
670 newinfo
->type
= MIMETYPE_APPLICATION
;
671 newinfo
->subtype
= g_strdup("octet-stream");
672 newinfo
->content
= MIMECONTENT_MEM
;
673 newinfo
->data
.mem
= g_malloc(len
+ 1);
675 memmove(newinfo
->data
.mem
, enccontent
, len
);
676 newinfo
->data
.mem
[len
] = '\0';
677 g_node_append(encmultipart
->node
, newinfo
->node
);
687 static PrivacySystem pgpmime_system
= {
689 "PGP MIME", /* name */
691 pgpmime_free_privacydata
, /* free_privacydata */
693 pgpmime_is_signed
, /* is_signed(MimeInfo *) */
694 pgpmime_check_sig_async
,
696 pgpmime_is_encrypted
, /* is_encrypted(MimeInfo *) */
697 pgpmime_decrypt
, /* decrypt(MimeInfo *) */
703 pgpmime_get_encrypt_data
,
705 pgpmime_get_encrypt_warning
,
706 pgpmime_inhibit_encrypt_warning
,
707 prefs_gpg_auto_check_signatures
,
712 privacy_register_system(&pgpmime_system
);
717 privacy_unregister_system(&pgpmime_system
);
720 struct PluginFeature
*plugin_provides(void)
722 static struct PluginFeature features
[] =
723 { {PLUGIN_PRIVACY
, N_("PGP/Mime")},
724 {PLUGIN_NOTHING
, NULL
}};
727 #endif /* USE_GPGME */