/*
elmo - ELectronic Mail Operator
Copyright (C) 2004 rzyjontko
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; version 2.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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.
----------------------------------------------------------------------
*/
/****************************************************************************
* IMPLEMENTATION HEADERS
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef HAVE_LOCALE_H
# include <locale.h>
#endif
#ifdef GPG_SUPPORT
# ifdef HAVE_GPGME4
# include <gpgme4/gpgme.h>
# else
# include <gpgme.h>
# endif
#endif
#include <string.h>
#include <unistd.h>
#include "pgp.h"
#include "read.h"
#include "gettext.h"
#include "ask.h"
#include "mime.h"
#include "xmalloc.h"
#include "file.h"
#include "mlex.h"
#include "rmime.h"
#include "hash.h"
#include "error.h"
#include "gettext.h"
/****************************************************************************
* IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS
****************************************************************************/
#define PREAMBLE do { if (! initialized) return; } while (0)
/****************************************************************************
* IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES
****************************************************************************/
/****************************************************************************
* IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES
****************************************************************************/
struct pass_list {
struct pass_list *next;
char *hint;
char *pass;
};
/****************************************************************************
* IMPLEMENTATION REQUIRED EXTERNAL REFERENCES (AVOID)
****************************************************************************/
/****************************************************************************
* IMPLEMENTATION PRIVATE DATA
****************************************************************************/
#ifdef GPG_SUPPORT
/* GPGME context */
static gpgme_ctx_t ctx;
#endif
#ifdef GPG_SUPPORT
/* How should we hide the password. */
static hide_mode_t hide = HIDE_ASTERISK;
#endif
static int initialized = 0;
/* List of passwords read by user. */
static struct pass_list *passwords = NULL;
/****************************************************************************
* INTERFACE DATA
****************************************************************************/
/****************************************************************************
* IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES
****************************************************************************/
/****************************************************************************
* IMPLEMENTATION PRIVATE FUNCTIONS
****************************************************************************/
static void
destroy_passwords (struct pass_list *list)
{
if (list == NULL)
return;
destroy_passwords (list->next);
xfree (list->hint);
xfree (list->pass);
xfree (list);
}
#ifdef GPG_SUPPORT
static void
add_password (const char *hint, const char *pass)
{
struct pass_list *list = xmalloc (sizeof (struct pass_list));
list->hint = xstrdup (hint);
list->pass = xstrdup (pass);
list->next = passwords;
passwords = list;
}
static char *
find_password (struct pass_list *list, const char *hint)
{
if (list == NULL)
return NULL;
if (strcmp (list->hint, hint) == 0)
return list->pass;
return find_password (list->next, hint);
}
static gpgme_error_t
passphrase_cb (void *hook, const char *hint, const char *info,
int prev_bad, int fd)
{
char *answer = NULL;
if (! prev_bad){
answer = find_password (passwords, hint);
}
if (answer == NULL){
answer = read_argument (_("Password: "), NULL, COMPLETE_NONE,
hide);
}
if (answer == NULL){
write (fd, "\n", 1);
return GPG_ERR_CANCELED;
}
write (fd, answer, strlen (answer));
write (fd, "\n", 1);
add_password (hint, answer);
return GPG_ERR_NO_ERROR;
}
static gpgme_data_t
data_from_mime (const char *fname, mime_t *mime)
{
gpgme_data_t result;
gpgme_data_new_from_filepart (& result, fname, NULL, mime->off_start,
mime->off_end - mime->off_start);
return result;
}
static gpgme_data_t
data_from_mime_with_header (const char *fname, mime_t *mime)
{
gpgme_data_t result;
gpgme_data_new_from_filepart (& result, fname, NULL, mime->off_header,
mime->off_end - mime->off_header);
return result;
}
static gpgme_data_t
data_from_tmp_file (FILE **fp, char **fname)
{
gpgme_data_t result;
*fp = file_temp_file (fname);
gpgme_data_new_from_stream (& result, *fp);
return result;
}
static mime_t *
parse_file (FILE *fp)
{
mime_t *mime;
rewind (fp);
yyin = fp;
if (mlex_scan_file (0) != NEXT_MAIL)
return NULL;
mime = newmail->mime->mime;
newmail->mime->mime = NULL;
mail_destroy (newmail, BOX_INVALID);
return mime;
}
static void
decrypt (mime_info_t *mime_info)
{
gpgme_data_t in, out;
FILE *fp;
char *fname;
in = data_from_mime (mime_info->file,
mime_info->mime->parts->array[1]);
out = data_from_tmp_file (& fp, & fname);
if (gpgme_op_decrypt (ctx, in, out) == GPG_ERR_NO_ERROR){
mime_info->decrypted = parse_file (fp);
mime_info->d_file = fname;
}
else {
xfree (fname);
destroy_passwords (passwords);
}
fclose (fp);
gpgme_data_release (in);
gpgme_data_release (out);
}
static char *
sig_from_fpr (const char *fpr)
{
gpgme_key_t key;
str_t *str;
char *name;
if (gpgme_get_key (ctx, fpr, & key, 0) != GPG_ERR_NO_ERROR)
return NULL;
name = mime_decode_utf8 (key->uids->name);
str = str_create ();
str_sprintf (str, "%s <%s>", name, key->uids->email);
xfree (name);
gpgme_key_unref (key);
return str_finished (str);
}
static void
get_sig_info (mime_info_t *mime)
{
gpgme_verify_result_t result = gpgme_op_verify_result (ctx);
gpgme_error_t error = result->signatures->status;
gpgme_sigsum_t summary = result->signatures->summary;
mime->sig_by = sig_from_fpr (result->signatures->fpr);
if (summary & GPGME_SIGSUM_VALID){
mime->sig = SIG_OK;
}
else if (summary & GPGME_SIGSUM_GREEN){
mime->sig = SIG_WARN;
mime->sig_text = xstrdup (gpgme_strerror (error));
}
else if (summary == 0){
mime->sig = SIG_WARN;
mime->sig_text = xstrdup (gpgme_strerror (error));
}
else {
mime->sig = SIG_FAIL;
mime->sig_text = xstrdup (gpgme_strerror (error));
}
}
static void
verify (mime_info_t *mime_info)
{
gpgme_data_t mail, sig;
gpgme_error_t err;
if (mime_info->sig != SIG_NOT_SIGNED)
return;
sig = data_from_mime (mime_info->file,
mime_info->mime->parts->array[1]);
mail = data_from_mime_with_header (mime_info->file,
mime_info->mime->parts->array[0]);
err = gpgme_op_verify (ctx, sig, mail, NULL);
if (err == GPG_ERR_NO_ERROR){
get_sig_info (mime_info);
}
else {
error_ (0, _("couldn't verify signature: %s"),
gpgme_strerror (err));
}
gpgme_data_release (sig);
gpgme_data_release (mail);
}
#else
static void
decrypt (mime_info_t *mime)
{
}
static void
verify (mime_info_t *mime)
{
}
#endif
/****************************************************************************
* INTERFACE FUNCTIONS
****************************************************************************/
void
pgp_init (void)
{
#if defined (GPG_SUPPORT) && defined (HAVE_LOCALE_H)
char *shide;
gpgme_check_version (NULL);
gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
gpgme_set_locale (NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL));
shide = ask_for_default ("hide_password", NULL);
if (shide && strcasecmp (shide, "yes") == 0)
hide = HIDE_EMPTY;
if (gpgme_new (& ctx) == GPG_ERR_NO_ERROR){
gpgme_set_protocol (ctx, GPGME_PROTOCOL_OpenPGP);
gpgme_set_armor (ctx, 1);
gpgme_set_passphrase_cb (ctx, passphrase_cb, NULL);
initialized = 1;
}
#endif
}
void
pgp_free_resources (void)
{
PREAMBLE;
#ifdef GPG_SUPPORT
gpgme_release (ctx);
destroy_passwords (passwords);
#endif
initialized = 0;
passwords = NULL;
}
void
pgp_decrypt_verify (mime_info_t *mime_info)
{
PREAMBLE;
if (strcasecmp (mime_info->mime->type, "multipart/encrypted") == 0){
decrypt (mime_info);
}
else if (strcasecmp (mime_info->mime->type, "multipart/signed") == 0){
verify (mime_info);
}
}
void
pgp_forget_passphrase (void)
{
destroy_passwords (passwords);
passwords = NULL;
error_ (0, "%s", _("passwords have been forgotten"));
}
/****************************************************************************
* INTERFACE CLASS BODIES
****************************************************************************/
/****************************************************************************
*
* END MODULE pgp.c
*
****************************************************************************/
syntax highlighted by Code2HTML, v. 0.9.1