X-Git-Url: http://git.claws-mail.org/?p=claws.git;a=blobdiff_plain;f=src%2Fplugins%2Fpgpmime%2Fpgpmime.c;h=b9a3d7dfef5ebda13768ee662e833214da36d350;hp=4a213de5f2299fab7dedd154305fcb7dfa973ecc;hb=81b0679965b506fce98bedb70f74f8df70e81312;hpb=d6cf9a129f0e407ecd2307ce28b368369fc6e549 diff --git a/src/plugins/pgpmime/pgpmime.c b/src/plugins/pgpmime/pgpmime.c index 4a213de5f..b9a3d7dfe 100644 --- a/src/plugins/pgpmime/pgpmime.c +++ b/src/plugins/pgpmime/pgpmime.c @@ -1,10 +1,10 @@ /* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2003 Hiroyuki Yamamoto & the Sylpheed-Claws team + * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2014 the Claws Mail team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, @@ -13,29 +13,36 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program. If not, see . + * */ #ifdef HAVE_CONFIG_H # include "config.h" +#include "claws-features.h" #endif #ifdef USE_GPGME #include "defs.h" #include +#include #include #include +#include #include "utils.h" #include "privacy.h" #include "procmime.h" +#include "plugin.h" + #include "pgpmime.h" -#include "sgpgme.h" +#include +#include +#include +#include + #include "prefs_common.h" -#include "prefs_gpg.h" -#include "passphrase.h" typedef struct _PrivacyDataPGP PrivacyDataPGP; @@ -45,8 +52,8 @@ struct _PrivacyDataPGP gboolean done_sigtest; gboolean is_signed; - GpgmeSigStat sigstatus; - GpgmeCtx ctx; + gpgme_verify_result_t sigstatus; + gpgme_ctx_t ctx; }; static PrivacySystem pgpmime_system; @@ -56,13 +63,17 @@ static gint pgpmime_check_signature(MimeInfo *mimeinfo); static PrivacyDataPGP *pgpmime_new_privacydata() { PrivacyDataPGP *data; + gpgme_error_t err; data = g_new0(PrivacyDataPGP, 1); data->data.system = &pgpmime_system; data->done_sigtest = FALSE; data->is_signed = FALSE; - data->sigstatus = GPGME_SIG_STAT_NONE; - gpgme_new(&data->ctx); + data->sigstatus = NULL; + if ((err = gpgme_new(&data->ctx)) != GPG_ERR_NO_ERROR) { + g_warning(_("Couldn't initialize GPG context, %s"), gpgme_strerror(err)); + return NULL; + } return data; } @@ -70,7 +81,7 @@ static PrivacyDataPGP *pgpmime_new_privacydata() static void pgpmime_free_privacydata(PrivacyData *_data) { PrivacyDataPGP *data = (PrivacyDataPGP *) _data; - + gpgme_release(data->ctx); g_free(data); } @@ -81,7 +92,7 @@ static gboolean pgpmime_is_signed(MimeInfo *mimeinfo) const gchar *protocol; PrivacyDataPGP *data = NULL; - g_return_val_if_fail(mimeinfo != NULL, FALSE); + cm_return_val_if_fail(mimeinfo != NULL, FALSE); if (mimeinfo->privacy != NULL) { data = (PrivacyDataPGP *) mimeinfo->privacy; if (data->done_sigtest) @@ -96,7 +107,8 @@ static gboolean pgpmime_is_signed(MimeInfo *mimeinfo) g_ascii_strcasecmp(parent->subtype, "signed")) return FALSE; protocol = procmime_mimeinfo_get_parameter(parent, "protocol"); - if ((protocol == NULL) || g_ascii_strcasecmp(protocol, "application/pgp-signature")) + if ((protocol == NULL) || + (g_ascii_strcasecmp(protocol, "application/pgp-signature"))) return FALSE; /* check if mimeinfo is the first child */ @@ -109,15 +121,17 @@ static gboolean pgpmime_is_signed(MimeInfo *mimeinfo) if (signature == NULL) return FALSE; if ((signature->type != MIMETYPE_APPLICATION) || - g_ascii_strcasecmp(signature->subtype, "pgp-signature")) + (g_ascii_strcasecmp(signature->subtype, "pgp-signature"))) return FALSE; if (data == NULL) { data = pgpmime_new_privacydata(); mimeinfo->privacy = (PrivacyData *) data; } - data->done_sigtest = TRUE; - data->is_signed = TRUE; + if (data != NULL) { + data->done_sigtest = TRUE; + data->is_signed = TRUE; + } return TRUE; } @@ -160,31 +174,60 @@ static gint pgpmime_check_signature(MimeInfo *mimeinfo) FILE *fp; gchar *boundary; gchar *textstr; - GpgmeData sigdata, textdata; - - g_return_val_if_fail(mimeinfo != NULL, -1); - g_return_val_if_fail(mimeinfo->privacy != NULL, -1); + gpgme_data_t sigdata = NULL, textdata = NULL; + gpgme_error_t err; + cm_return_val_if_fail(mimeinfo != NULL, -1); + cm_return_val_if_fail(mimeinfo->privacy != NULL, -1); data = (PrivacyDataPGP *) mimeinfo->privacy; + if ((err = gpgme_new(&data->ctx)) != GPG_ERR_NO_ERROR) { + debug_print(("Couldn't initialize GPG context, %s"), gpgme_strerror(err)); + privacy_set_error(_("Couldn't initialize GPG context, %s"), gpgme_strerror(err)); + return 0; + } + debug_print("Checking PGP/MIME signature\n"); + + err = gpgme_set_protocol(data->ctx, GPGME_PROTOCOL_OpenPGP); + + if (err) { + debug_print ("gpgme_set_protocol failed: %s\n", + gpgme_strerror (err)); + } parent = procmime_mimeinfo_parent(mimeinfo); - fp = fopen(parent->data.filename, "rb"); - g_return_val_if_fail(fp != NULL, SIGNATURE_INVALID); + fp = g_fopen(parent->data.filename, "rb"); + cm_return_val_if_fail(fp != NULL, SIGNATURE_INVALID); boundary = g_hash_table_lookup(parent->typeparameters, "boundary"); - if (!boundary) + if (!boundary) { + privacy_set_error(_("Signature boundary not found.")); + fclose(fp); return 0; - + } textstr = get_canonical_content(fp, boundary); - gpgme_data_new_from_mem(&textdata, textstr, strlen(textstr), 0); + err = gpgme_data_new_from_mem(&textdata, textstr, (size_t)strlen(textstr), 0); + if (err) { + debug_print ("gpgme_data_new_from_mem failed: %s\n", + gpgme_strerror (err)); + } signature = (MimeInfo *) mimeinfo->node->next->data; sigdata = sgpgme_data_from_mimeinfo(signature); - data->sigstatus = - sgpgme_verify_signature (data->ctx, sigdata, textdata); + err = 0; + if (signature->encoding_type == ENC_BASE64) { + err = gpgme_data_set_encoding (sigdata, GPGME_DATA_ENCODING_BASE64); + } + if (err) { + debug_print ("gpgme_data_set_encoding failed: %s\n", + gpgme_strerror (err)); + } + + data->sigstatus = + sgpgme_verify_signature (data->ctx, sigdata, textdata, NULL); + gpgme_data_release(sigdata); gpgme_data_release(textdata); g_free(textstr); @@ -197,12 +240,8 @@ static SignatureStatus pgpmime_get_sig_status(MimeInfo *mimeinfo) { PrivacyDataPGP *data = (PrivacyDataPGP *) mimeinfo->privacy; - g_return_val_if_fail(data != NULL, SIGNATURE_INVALID); + cm_return_val_if_fail(data != NULL, SIGNATURE_INVALID); - if (data->sigstatus == GPGME_SIG_STAT_NONE && - prefs_gpg_get_config()->auto_check_signatures) - pgpmime_check_signature(mimeinfo); - return sgpgme_sigstat_gpgme_to_privacy(data->ctx, data->sigstatus); } @@ -210,25 +249,17 @@ static gchar *pgpmime_get_sig_info_short(MimeInfo *mimeinfo) { PrivacyDataPGP *data = (PrivacyDataPGP *) mimeinfo->privacy; - g_return_val_if_fail(data != NULL, g_strdup("Error")); + cm_return_val_if_fail(data != NULL, g_strdup("Error")); - if (data->sigstatus == GPGME_SIG_STAT_NONE && - prefs_gpg_get_config()->auto_check_signatures) - pgpmime_check_signature(mimeinfo); - return sgpgme_sigstat_info_short(data->ctx, data->sigstatus); } static gchar *pgpmime_get_sig_info_full(MimeInfo *mimeinfo) { PrivacyDataPGP *data = (PrivacyDataPGP *) mimeinfo->privacy; - - g_return_val_if_fail(data != NULL, g_strdup("Error")); - if (data->sigstatus == GPGME_SIG_STAT_NONE && - prefs_gpg_get_config()->auto_check_signatures) - pgpmime_check_signature(mimeinfo); - + cm_return_val_if_fail(data != NULL, g_strdup("Error")); + return sgpgme_sigstat_info_full(data->ctx, data->sigstatus); } @@ -236,7 +267,10 @@ static gboolean pgpmime_is_encrypted(MimeInfo *mimeinfo) { MimeInfo *tmpinfo; const gchar *tmpstr; - + const gchar *begin_indicator = "-----BEGIN PGP MESSAGE-----"; + const gchar *end_indicator = "-----END PGP MESSAGE-----"; + gchar *textdata; + if (mimeinfo->type != MIMETYPE_MULTIPART) return FALSE; if (g_ascii_strcasecmp(mimeinfo->subtype, "encrypted")) @@ -259,27 +293,45 @@ static gboolean pgpmime_is_encrypted(MimeInfo *mimeinfo) if (g_ascii_strcasecmp(tmpinfo->subtype, "octet-stream")) return FALSE; + textdata = get_part_as_string(tmpinfo); + if (!textdata) + return FALSE; + + if (!pgp_locate_armor_header(textdata, begin_indicator)) { + g_free(textdata); + return FALSE; + } + if (!pgp_locate_armor_header(textdata, end_indicator)) { + g_free(textdata); + return FALSE; + } + + g_free(textdata); + return TRUE; } static MimeInfo *pgpmime_decrypt(MimeInfo *mimeinfo) { MimeInfo *encinfo, *decinfo, *parseinfo; - GpgmeData cipher, plain; + gpgme_data_t cipher = NULL, plain = NULL; static gint id = 0; FILE *dstfp; - gint nread; gchar *fname; - gchar buf[BUFFSIZE]; - GpgmeSigStat sigstat = 0; + gpgme_verify_result_t sigstat = NULL; PrivacyDataPGP *data = NULL; - GpgmeCtx ctx; - - if (gpgme_new(&ctx) != GPGME_No_Error) + gpgme_ctx_t ctx; + gchar *chars; + size_t len; + gpgme_error_t err; + + if ((err = gpgme_new(&ctx)) != GPG_ERR_NO_ERROR) { + debug_print(("Couldn't initialize GPG context, %s"), gpgme_strerror(err)); + privacy_set_error(_("Couldn't initialize GPG context, %s"), gpgme_strerror(err)); return NULL; - + } - g_return_val_if_fail(pgpmime_is_encrypted(mimeinfo), NULL); + cm_return_val_if_fail(pgpmime_is_encrypted(mimeinfo), NULL); encinfo = (MimeInfo *) g_node_nth_child(mimeinfo->node, 1)->data; @@ -287,132 +339,130 @@ static MimeInfo *pgpmime_decrypt(MimeInfo *mimeinfo) plain = sgpgme_decrypt_verify(cipher, &sigstat, ctx); gpgme_data_release(cipher); - if (plain == NULL) + if (plain == NULL) { + debug_print("plain is null!\n"); + gpgme_release(ctx); return NULL; - + } + fname = g_strdup_printf("%s%cplaintext.%08x", get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id); - if ((dstfp = fopen(fname, "wb")) == NULL) { + if ((dstfp = g_fopen(fname, "wb")) == NULL) { FILE_OP_ERROR(fname, "fopen"); + privacy_set_error(_("Couldn't open decrypted file %s"), fname); g_free(fname); gpgme_data_release(plain); + gpgme_release(ctx); + debug_print("can't open!\n"); return NULL; } - fprintf(dstfp, "MIME-Version: 1.0\n"); - gpgme_data_rewind (plain); - while (gpgme_data_read(plain, buf, sizeof(buf), &nread) == GPGME_No_Error) { - fwrite (buf, nread, 1, dstfp); + if (fprintf(dstfp, "MIME-Version: 1.0\n") < 0) { + FILE_OP_ERROR(fname, "fprintf"); + fclose(dstfp); + privacy_set_error(_("Couldn't write to decrypted file %s"), fname); + g_free(fname); + gpgme_data_release(plain); + gpgme_release(ctx); + debug_print("can't open!\n"); + return NULL; + } + + chars = sgpgme_data_release_and_get_mem(plain, &len); + if (len > 0) { + if (fwrite(chars, 1, len, dstfp) < len) { + FILE_OP_ERROR(fname, "fwrite"); + g_free(chars); + fclose(dstfp); + privacy_set_error(_("Couldn't write to decrypted file %s"), fname); + g_free(fname); + gpgme_data_release(plain); + gpgme_release(ctx); + debug_print("can't open!\n"); + return NULL; + } + } + g_free(chars); + + if (fclose(dstfp) == EOF) { + FILE_OP_ERROR(fname, "fclose"); + privacy_set_error(_("Couldn't close decrypted file %s"), fname); + g_free(fname); + gpgme_data_release(plain); + gpgme_release(ctx); + debug_print("can't open!\n"); + return NULL; } - fclose(dstfp); - - gpgme_data_release(plain); parseinfo = procmime_scan_file(fname); g_free(fname); - if (parseinfo == NULL) + if (parseinfo == NULL) { + gpgme_release(ctx); + privacy_set_error(_("Couldn't parse decrypted file.")); return NULL; + } decinfo = g_node_first_child(parseinfo->node) != NULL ? g_node_first_child(parseinfo->node)->data : NULL; - if (decinfo == NULL) + if (decinfo == NULL) { + privacy_set_error(_("Couldn't parse decrypted file parts.")); + gpgme_release(ctx); return NULL; + } g_node_unlink(decinfo->node); procmime_mimeinfo_free_all(parseinfo); decinfo->tmp = TRUE; - if (sigstat != GPGME_SIG_STAT_NONE) { + if (sigstat != NULL && sigstat->signatures != NULL) { if (decinfo->privacy != NULL) { data = (PrivacyDataPGP *) decinfo->privacy; } else { data = pgpmime_new_privacydata(); decinfo->privacy = (PrivacyData *) data; } - data->done_sigtest = TRUE; - data->is_signed = TRUE; - data->sigstatus = sigstat; - if (data->ctx) - gpgme_release(data->ctx); - data->ctx = ctx; + if (data != NULL) { + data->done_sigtest = TRUE; + data->is_signed = TRUE; + data->sigstatus = sigstat; + if (data->ctx) + gpgme_release(data->ctx); + data->ctx = ctx; + } } else gpgme_release(ctx); return decinfo; } -/* - * Find TAG in XML and return a pointer into xml set just behind the - * closing angle. Return NULL if not found. - */ -static const char * -find_xml_tag (const char *xml, const char *tag) -{ - int taglen = strlen (tag); - const char *s = xml; - - while ( (s = strchr (s, '<')) ) { - s++; - if (!strncmp (s, tag, taglen)) { - const char *s2 = s + taglen; - if (*s2 == '>' || isspace (*(const unsigned char*)s2) ) { - /* found */ - while (*s2 && *s2 != '>') /* skip attributes */ - s2++; - /* fixme: do need to handle angles inside attribute vallues? */ - return *s2? (s2+1):NULL; - } - } - while (*s && *s != '>') /* skip to end of tag */ - s++; - } - return NULL; -} - - -/* - * Extract the micalg from an GnupgOperationInfo XML container. - */ -static char * -extract_micalg (char *xml) -{ - const char *s; - - s = find_xml_tag (xml, "GnupgOperationInfo"); - if (s) { - const char *s_end = find_xml_tag (s, "/GnupgOperationInfo"); - s = find_xml_tag (s, "signature"); - if (s && s_end && s < s_end) { - const char *s_end2 = find_xml_tag (s, "/signature"); - if (s_end2 && s_end2 < s_end) { - s = find_xml_tag (s, "micalg"); - if (s && s < s_end2) { - s_end = strchr (s, '<'); - if (s_end) { - char *p = g_malloc (s_end - s + 1); - memcpy (p, s, s_end - s); - p[s_end-s] = 0; - return p; - } - } - } - } - } - return NULL; -} - -gboolean pgpmime_sign(MimeInfo *mimeinfo) +gboolean pgpmime_sign(MimeInfo *mimeinfo, PrefsAccount *account, const gchar *from_addr) { MimeInfo *msgcontent, *sigmultipart, *newinfo; - gchar *textstr, *opinfo, *micalg; + gchar *textstr, *micalg = NULL; FILE *fp; - gchar *boundary, *sigcontent; - GpgmeCtx ctx; - GpgmeData gpgtext, gpgsig; - guint len; + gchar *boundary = NULL; + gchar *sigcontent; + gpgme_ctx_t ctx; + gpgme_data_t gpgtext, gpgsig; + gpgme_error_t err; + size_t len; struct passphrase_cb_info_s info; - + gpgme_sign_result_t result = NULL; + gchar *test_msg; + + fp = my_tmpfile(); + if (fp == NULL) { + privacy_set_error(_("Couldn't create temporary file: %s"), strerror(errno)); + return FALSE; + } + procmime_write_mimeinfo(mimeinfo, fp); + rewind(fp); + + /* read temporary file into memory */ + test_msg = file_read_stream_to_str(fp); + fclose(fp); + memset (&info, 0, sizeof info); /* remove content node from message */ @@ -423,7 +473,14 @@ gboolean pgpmime_sign(MimeInfo *mimeinfo) sigmultipart = procmime_mimeinfo_new(); sigmultipart->type = MIMETYPE_MULTIPART; sigmultipart->subtype = g_strdup("signed"); - boundary = generate_mime_boundary("Signature"); + + do { + g_free(boundary); + boundary = generate_mime_boundary("Sig"); + } while (strstr(test_msg, boundary) != NULL); + + g_free(test_msg); + g_hash_table_insert(sigmultipart->typeparameters, g_strdup("boundary"), g_strdup(boundary)); g_hash_table_insert(sigmultipart->typeparameters, g_strdup("protocol"), @@ -433,6 +490,11 @@ gboolean pgpmime_sign(MimeInfo *mimeinfo) /* write message content to temporary file */ fp = my_tmpfile(); + if (fp == NULL) { + perror("my_tmpfile"); + privacy_set_error(_("Couldn't create temporary file: %s"), strerror(errno)); + return FALSE; + } procmime_write_mimeinfo(sigmultipart, fp); rewind(fp); @@ -441,29 +503,88 @@ gboolean pgpmime_sign(MimeInfo *mimeinfo) fclose(fp); - gpgme_data_new_from_mem(&gpgtext, textstr, strlen(textstr), 0); + gpgme_data_new_from_mem(&gpgtext, textstr, (size_t)strlen(textstr), 0); gpgme_data_new(&gpgsig); - gpgme_new(&ctx); + if ((err = gpgme_new(&ctx)) != GPG_ERR_NO_ERROR) { + debug_print(("Couldn't initialize GPG context, %s"), gpgme_strerror(err)); + privacy_set_error(_("Couldn't initialize GPG context, %s"), gpgme_strerror(err)); + return FALSE; + } gpgme_set_textmode(ctx, 1); gpgme_set_armor(ctx, 1); - gpgme_signers_clear(ctx); + gpgme_signers_clear (ctx); - if (!getenv("GPG_AGENT_INFO")) { - info.c = ctx; + if (!sgpgme_setup_signers(ctx, account, from_addr)) { + gpgme_release(ctx); + return FALSE; + } + + prefs_gpg_enable_agent(prefs_gpg_get_config()->use_gpg_agent); + if (getenv("GPG_AGENT_INFO") && prefs_gpg_get_config()->use_gpg_agent) { + debug_print("GPG_AGENT_INFO environment defined, running without passphrase callback\n"); + } else { + info.c = ctx; gpgme_set_passphrase_cb (ctx, gpgmegtk_passphrase_cb, &info); } - if (gpgme_op_sign(ctx, gpgtext, gpgsig, GPGME_SIG_MODE_DETACH) != GPGME_No_Error) + err = gpgme_op_sign(ctx, gpgtext, gpgsig, GPGME_SIG_MODE_DETACH); + if (err != GPG_ERR_NO_ERROR) { + if (err == GPG_ERR_CANCELED) { + /* ignore cancelled signing */ + privacy_reset_error(); + debug_print("gpgme_op_sign cancelled\n"); + } else { + privacy_set_error(_("Data signing failed, %s"), gpgme_strerror(err)); + debug_print("gpgme_op_sign error : %x\n", err); + } + gpgme_release(ctx); return FALSE; - opinfo = gpgme_get_op_info(ctx, 0); - micalg = extract_micalg(opinfo); - g_free(opinfo); + } + result = gpgme_op_sign_result(ctx); + if (result && result->signatures) { + gpgme_new_signature_t sig = result->signatures; + if (gpgme_get_protocol(ctx) == GPGME_PROTOCOL_OpenPGP) { + micalg = g_strdup_printf("pgp-%s", g_ascii_strdown(gpgme_hash_algo_name( + result->signatures->hash_algo),-1)); + } else { + micalg = g_strdup(gpgme_hash_algo_name( + result->signatures->hash_algo)); + } + while (sig) { + debug_print("valid signature: %s\n", sig->fpr); + sig = sig->next; + } + } else if (result && result->invalid_signers) { + gpgme_invalid_key_t invalid = result->invalid_signers; + while (invalid) { + g_warning("invalid signer: %s (%s)", invalid->fpr, + gpgme_strerror(invalid->reason)); + privacy_set_error(_("Data signing failed due to invalid signer: %s"), + gpgme_strerror(invalid->reason)); + invalid = invalid->next; + } + gpgme_release(ctx); + return FALSE; + } else { + /* can't get result (maybe no signing key?) */ + debug_print("gpgme_op_sign_result error\n"); + privacy_set_error(_("Data signing failed, no results.")); + gpgme_release(ctx); + return FALSE; + } - gpgme_release(ctx); - sigcontent = gpgme_data_release_and_get_mem(gpgsig, &len); + sigcontent = sgpgme_data_release_and_get_mem(gpgsig, &len); gpgme_data_release(gpgtext); g_free(textstr); + if (sigcontent == NULL || len <= 0) { + g_warning("sgpgme_data_release_and_get_mem failed"); + privacy_set_error(_("Data signing failed, no contents.")); + g_free(micalg); + g_free(sigcontent); + return FALSE; + } + /* add signature */ g_hash_table_insert(sigmultipart->typeparameters, g_strdup("micalg"), micalg); @@ -471,19 +592,38 @@ gboolean pgpmime_sign(MimeInfo *mimeinfo) newinfo = procmime_mimeinfo_new(); newinfo->type = MIMETYPE_APPLICATION; newinfo->subtype = g_strdup("pgp-signature"); + newinfo->description = g_strdup(_("OpenPGP digital signature")); newinfo->content = MIMECONTENT_MEM; - newinfo->data.mem = g_memdup(sigcontent, len + 1); + newinfo->data.mem = g_malloc(len + 1); + g_memmove(newinfo->data.mem, sigcontent, len); newinfo->data.mem[len] = '\0'; g_node_append(sigmultipart->node, newinfo->node); g_free(sigcontent); + gpgme_release(ctx); return TRUE; } - gchar *pgpmime_get_encrypt_data(GSList *recp_names) { - return sgpgme_get_encrypt_data(recp_names); + return sgpgme_get_encrypt_data(recp_names, GPGME_PROTOCOL_OpenPGP); +} + +static const gchar *pgpmime_get_encrypt_warning(void) +{ + if (prefs_gpg_should_skip_encryption_warning(pgpmime_system.id)) + return NULL; + else + return _("Please note that email headers, like Subject, " + "are not encrypted by the PGP/Mime system."); +} + +static void pgpmime_inhibit_encrypt_warning(gboolean inhibit) +{ + if (inhibit) + prefs_gpg_add_skip_encryption_warning(pgpmime_system.id); + else + prefs_gpg_remove_skip_encryption_warning(pgpmime_system.id); } gboolean pgpmime_encrypt(MimeInfo *mimeinfo, const gchar *encrypt_data) @@ -491,23 +631,42 @@ gboolean pgpmime_encrypt(MimeInfo *mimeinfo, const gchar *encrypt_data) MimeInfo *msgcontent, *encmultipart, *newinfo; FILE *fp; gchar *boundary, *enccontent; - guint len; + size_t len; gchar *textstr; - GpgmeData gpgtext, gpgenc; - gchar **recipients, **nextrecp; - GpgmeRecipients recp; - GpgmeCtx ctx; - - /* build GpgmeRecipients from encrypt_data */ - recipients = g_strsplit(encrypt_data, " ", 0); - gpgme_recipients_new(&recp); - for (nextrecp = recipients; *nextrecp != NULL; nextrecp++) { - printf("%s\n", *nextrecp); - gpgme_recipients_add_name_with_validity(recp, *nextrecp, - GPGME_VALIDITY_FULL); + gpgme_data_t gpgtext = NULL, gpgenc = NULL; + gpgme_ctx_t ctx = NULL; + gpgme_key_t *kset = NULL; + gchar **fprs = g_strsplit(encrypt_data, " ", -1); + gint i = 0; + gpgme_error_t err; + + while (fprs[i] && strlen(fprs[i])) { + i++; } - g_strfreev(recipients); - + + kset = g_malloc(sizeof(gpgme_key_t)*(i+1)); + memset(kset, 0, sizeof(gpgme_key_t)*(i+1)); + if ((err = gpgme_new(&ctx)) != GPG_ERR_NO_ERROR) { + debug_print(("Couldn't initialize GPG context, %s"), gpgme_strerror(err)); + privacy_set_error(_("Couldn't initialize GPG context, %s"), gpgme_strerror(err)); + g_free(kset); + return FALSE; + } + i = 0; + while (fprs[i] && strlen(fprs[i])) { + gpgme_key_t key; + err = gpgme_get_key(ctx, fprs[i], &key, 0); + if (err) { + debug_print("can't add key '%s'[%d] (%s)\n", fprs[i],i, gpgme_strerror(err)); + privacy_set_error(_("Couldn't add GPG key %s, %s"), fprs[i], gpgme_strerror(err)); + g_free(kset); + return FALSE; + } + debug_print("found %s at %d\n", fprs[i], i); + kset[i] = key; + i++; + } + debug_print("Encrypting message content\n"); /* remove content node from message */ @@ -527,6 +686,11 @@ gboolean pgpmime_encrypt(MimeInfo *mimeinfo, const gchar *encrypt_data) /* write message content to temporary file */ fp = my_tmpfile(); + if (fp == NULL) { + privacy_set_error(_("Couldn't create temporary file, %s"), strerror(errno)); + g_free(kset); + return FALSE; + } procmime_write_mimeinfo(encmultipart, fp); rewind(fp); @@ -536,18 +700,25 @@ gboolean pgpmime_encrypt(MimeInfo *mimeinfo, const gchar *encrypt_data) fclose(fp); /* encrypt data */ - gpgme_data_new_from_mem(&gpgtext, textstr, strlen(textstr), 0); + gpgme_data_new_from_mem(&gpgtext, textstr, (size_t)strlen(textstr), 0); gpgme_data_new(&gpgenc); - gpgme_new(&ctx); gpgme_set_armor(ctx, 1); + cm_gpgme_data_rewind(gpgtext); + + err = gpgme_op_encrypt(ctx, kset, GPGME_ENCRYPT_ALWAYS_TRUST, gpgtext, gpgenc); - gpgme_op_encrypt(ctx, recp, gpgtext, gpgenc); - - gpgme_release(ctx); - enccontent = gpgme_data_release_and_get_mem(gpgenc, &len); - gpgme_recipients_release(recp); + enccontent = sgpgme_data_release_and_get_mem(gpgenc, &len); gpgme_data_release(gpgtext); g_free(textstr); + g_free(kset); + + if (enccontent == NULL || len <= 0) { + g_warning("sgpgme_data_release_and_get_mem failed"); + privacy_set_error(_("Encryption failed, %s"), gpgme_strerror(err)); + gpgme_release(ctx); + g_free(enccontent); + return FALSE; + } /* create encrypted multipart */ g_node_unlink(msgcontent->node); @@ -565,18 +736,20 @@ gboolean pgpmime_encrypt(MimeInfo *mimeinfo, const gchar *encrypt_data) newinfo->type = MIMETYPE_APPLICATION; newinfo->subtype = g_strdup("octet-stream"); newinfo->content = MIMECONTENT_MEM; - newinfo->data.mem = g_memdup(enccontent, len + 1); + newinfo->data.mem = g_malloc(len + 1); + g_memmove(newinfo->data.mem, enccontent, len); newinfo->data.mem[len] = '\0'; g_node_append(encmultipart->node, newinfo->node); g_free(enccontent); + gpgme_release(ctx); return TRUE; } static PrivacySystem pgpmime_system = { "pgpmime", /* id */ - "PGP Mime", /* name */ + "PGP MIME", /* name */ pgpmime_free_privacydata, /* free_privacydata */ @@ -595,6 +768,9 @@ static PrivacySystem pgpmime_system = { TRUE, pgpmime_get_encrypt_data, pgpmime_encrypt, + pgpmime_get_encrypt_warning, + pgpmime_inhibit_encrypt_warning, + prefs_gpg_auto_check_signatures, }; void pgpmime_init() @@ -607,4 +783,11 @@ void pgpmime_done() privacy_unregister_system(&pgpmime_system); } +struct PluginFeature *plugin_provides(void) +{ + static struct PluginFeature features[] = + { {PLUGIN_PRIVACY, N_("PGP/Mime")}, + {PLUGIN_NOTHING, NULL}}; + return features; +} #endif /* USE_GPGME */