diff -ruN balsa-1.3.5/acconfig.h balsa-1.3.5-patch/acconfig.h --- balsa-1.3.5/acconfig.h Tue Mar 19 10:25:27 2002 +++ balsa-1.3.5-patch/acconfig.h Thu Apr 25 18:58:19 2002 @@ -52,6 +52,9 @@ /* use pcre instead of `standard' rexexp's? */ #undef HAVE_PCRE +/* Define if you use GPGME to support OpenPGP */ +#undef USE_GPGME + /* If Gnome-VFS available? */ #undef HAVE_GNOME_VFS diff -ruN balsa-1.3.5/configure.in balsa-1.3.5-patch/configure.in --- balsa-1.3.5/configure.in Tue Apr 23 13:22:49 2002 +++ balsa-1.3.5-patch/configure.in Thu Apr 25 18:58:27 2002 @@ -253,6 +253,49 @@ fi dnl ########################################################################## +dnl Check for GPGME if available +dnl ########################################################################## + +AC_ARG_ENABLE(gpgme, + [ --enable-gpgme Enable GnuPG support using GPGME [default=no]], + [ + ac_cv_enable_gpgme=$enableval + ],[ + ac_cv_enable_gpgme=no +]) + +AC_MSG_CHECKING(whether to use GPGME) +if test $ac_cv_enable_gpgme = yes; then + if gpgme-config --libs > /dev/null 2>&1; then + vers=`gpgme-config --version` + case $vers + in + 0.3.*) + have_gpgme=yes ;; + *) + have_gpgme=no ;; + esac + else + have_gpgme=no + fi + + if test x"$have_gpgme" = xyes ; then + AC_MSG_RESULT(yes) + CFLAGS="$CFLAGS `gpgme-config --cflags`" + LIBS="$LIBS `gpgme-config --libs`" + AC_DEFINE(USE_GPGME) + else + AC_MSG_RESULT(not found) + fi +else + have_gpgme=no + AC_MSG_RESULT(no) +fi + +AC_SUBST(GPGME_CFLAGS) +AC_SUBST(GPGME_LIBS) + +dnl ########################################################################## dnl Check for GtkHTML if available dnl ########################################################################## @@ -632,6 +675,7 @@ echo " Enable compile warnings: $set_more_warnings" echo " Use ESMTP : $with_esmtp" echo " Use GtkHTML: $have_gtkhtml" +echo " Use GPGME: $have_gpgme" echo " Use LDAP: $use_ldap" echo " Use GSS: $with_gss" echo " Use SSL: $with_ssl" diff -ruN balsa-1.3.5/libbalsa/Makefile.am balsa-1.3.5-patch/libbalsa/Makefile.am --- balsa-1.3.5/libbalsa/Makefile.am Mon Feb 25 16:41:50 2002 +++ balsa-1.3.5-patch/libbalsa/Makefile.am Thu Apr 25 18:59:58 2002 @@ -43,6 +43,8 @@ files.h \ folder-scanners.c \ folder-scanners.h \ + gpg.c \ + gpg.h \ identity.c \ identity.h \ information.c \ diff -ruN balsa-1.3.5/libbalsa/gpg.c balsa-1.3.5-patch/libbalsa/gpg.c --- balsa-1.3.5/libbalsa/gpg.c Thu Jan 1 01:00:00 1970 +++ balsa-1.3.5-patch/libbalsa/gpg.c Thu Apr 25 18:58:59 2002 @@ -0,0 +1,551 @@ +/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */ +/* Balsa E-Mail Client + * Copyright (C) 1997-2001 Stuart Parmenter and others, + * See the file AUTHORS for a list. + * + * 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, or (at your option) + * any later version. + * + * 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. + */ + +/* PGP/GPG support with GPGME library + * Laurent Cheylus - + */ + +#include "config.h" + +#ifdef USE_GPGME + +#include +#include +#include +#include + +#include + +#include + +#include "libbalsa.h" +#include "src/balsa-app.h" +#include "src/sendmsg-window.h" +#include "gpg.h" + +#define fail_if_err(a) do { if(a) { \ + fprintf (stderr, "%s:%d: GpgmeError %s\n", \ + __FILE__, __LINE__, gpgme_strerror(a)); \ + exit (1); } \ + } while(0) + +/* Recup SYLPHEED passphrase.c */ +struct passphrase_cb_info_s { + GpgmeCtx c; + int did_it; +}; + +static char * status_string(GpgmeSigStat status); +static void sig_status_for_key(GString *str,GpgmeCtx ctx,GpgmeSigStat status, + GpgmeKey key,const gchar *fpr); +static char * sig_status_full(GpgmeCtx ctx,GpgmeSigStat status); + +static void print_data(GpgmeData dh); +static void read_data(char * data, GpgmeData dh); + +static void text_sign_extract (gchar *msg,gchar *text,gchar *sign); +static void suppress_msg_headers(gchar *text_no_hd,gchar *text); +static void suppress_dashescape(gchar *text_no_dash,gchar *text); +static void string_crlf(gchar *new, gchar *string); +static gint linelen (const gchar *s); +static void handle_password(gchar *string,gchar **target); +static gchar* create_description (const gchar *desc); +static const char * passphrase_cb(void *opaque,const char *desc,void **r_hd); + +static char * +status_string (GpgmeSigStat status) +{ + const gchar *result; + + switch (status) { + case GPGME_SIG_STAT_NONE: + result = (gchar*) _("Oops: Signature not verified"); + break; + case GPGME_SIG_STAT_NOSIG: + result = (gchar*) _("No signature found"); + break; + case GPGME_SIG_STAT_GOOD: + result = (gchar*) _("Good signature from \"%s\""); + break; + case GPGME_SIG_STAT_BAD: + result = (gchar*) _("BAD signature from \"%s\""); + break; + case GPGME_SIG_STAT_NOKEY: + result = (gchar*) _("No public key to verify the signature"); + break; + case GPGME_SIG_STAT_ERROR: + result = (gchar*) _("Error verifying the signature"); + break; + case GPGME_SIG_STAT_DIFF: + result = (gchar*) _("Different results for signatures"); + break; + default: + result = (gchar*) _("Error: Unknown status"); + break; + } + + return (gchar*) result; +} + +static void +sig_status_for_key(GString *str, GpgmeCtx ctx, GpgmeSigStat status, + GpgmeKey key, const gchar *fpr) +{ + gint idx = 0; + const char *uid; + + uid = gpgme_key_get_string_attr (key, GPGME_ATTR_USERID, NULL, idx); + if (uid == NULL) { + g_string_sprintfa (str, "%s\n", + status_string (status)); + if ((fpr != NULL) && (*fpr != '\0')) + g_string_sprintfa (str, "Key fingerprint: %s\n", fpr); + g_string_append (str, (gchar*) _("Cannot find user ID for this key.")); + return; + } + g_string_sprintfa (str, status_string (status), uid); + g_string_append (str, "\n"); + + while (1) { + uid = gpgme_key_get_string_attr (key, GPGME_ATTR_USERID, + NULL, ++idx); + if (uid == NULL) + break; + g_string_sprintfa (str, (gchar*) _(" aka \"%s\"\n"), + uid); + } +} + +static char * +sig_status_full (GpgmeCtx ctx, GpgmeSigStat status) +{ + GString *str; + gint sig_idx = 0; + GpgmeError err; + GpgmeKey key; + const char *fpr; + time_t created; + struct tm *ctime_val; + char ctime_str[80]; + gchar *retval; + + str = g_string_new (""); + + fpr = gpgme_get_sig_status (ctx, sig_idx, &status, &created); + while (fpr != NULL) { + if (created != 0) { + ctime_val = localtime (&created); + strftime (ctime_str, sizeof (ctime_str), "%c", + ctime_val); + g_string_sprintfa (str, + (gchar*) _("Signature made %s\n"), + ctime_str); + } + err = gpgme_get_sig_key (ctx, sig_idx, &key); + if (err != 0) { + g_string_sprintfa (str, "%s\n", + status_string (status)); + if ((fpr != NULL) && (*fpr != '\0')) + g_string_sprintfa (str, + (gchar*) _("Key fingerprint: %s"), + fpr); + } else { + sig_status_for_key (str, ctx, status, key, fpr); + gpgme_key_unref (key); + } + + fpr = gpgme_get_sig_status (ctx, ++sig_idx, &status, &created); + } + + retval = str->str; + g_string_free (str, FALSE); + return retval; +} + +static void +text_sign_extract (gchar * msg, gchar * text, gchar * sign) +{ + gchar begin_sign[] = "-----BEGIN PGP SIGNATURE-----\n"; + gchar end_sign[] = "-----END PGP SIGNATURE-----\n"; + gchar begin_msg[] = "-----BEGIN PGP SIGNED MESSAGE-----\n"; + gchar *index=NULL; + + for (index=msg; *index != '\0'; index++) { + if (strncmp(index,begin_sign,strlen(begin_sign))==0) { + /* RFC 2440 + * The line ending (i.e. the ) before the + * '-----BEGIN PGP SIGNATURE-----' line that terminates + * the signed text is not considered part of the signed text. + */ + text--; + *text='\0'; + break; + } + else { + *text=*index; + text++; + } + } + + for (; *index != '\0'; index++) { + if (strncmp(index,end_sign,strlen(end_sign))==0) { + *sign='\0'; + break; + } + else { + *sign=*index; + sign++; + } + } + strncat(sign,end_sign,strlen(end_sign)); +} + +/* Suppress PGP/GPG headers of a PGP signed message */ +static void suppress_msg_headers(gchar * text_no_hd, gchar * text) +{ + gchar begin_msg[] = "-----BEGIN PGP SIGNED MESSAGE-----\n"; + gchar hash[] = "Hash:"; + gchar *index=NULL; + gint is_hash=0; + gint is_header=1; + + /* PGP/GPG signed message's headers : + * + * [\n...] + * -----BEGIN PGP SIGNED MESSAGE----- + * Hash: ... + * [Hash: ...] + * + * "DASH ESCAPED" CLEAR TEXT + * -----BEGIN PGP SIGNATURE----- + * ... + * + * OR + * + * [\n...] + * -----BEGIN PGP SIGNED MESSAGE----- + * + * "DASH ESCAPED" CLEAR TEXT + * -----BEGIN PGP SIGNATURE----- + * ... + * + */ + + index=text; + + /* Jump all first lines "\n" */ + while (strncmp(index,begin_msg,strlen(begin_msg))!=0) { + index++; + } + index=index+strlen(begin_msg); + + for (; *index != '\0'; index++) { + /* Case with headers "Hash:" */ + if (strncmp(index,hash,strlen(hash))==0) { + index+=strlen(hash); + while (*index != '\n' && *index != '\0') { + index++; + } + if (*index == '\n') { + is_hash=1; + } + } + else if (*index == '\n' && is_hash==1) { + is_hash=0; + is_header=0; + } + /* Case with no header "Hash:" */ + else if (*index == '\n' && is_header==1) { + is_header=0; + } + else { + *text_no_hd=*index; + text_no_hd++; + } + } + *text_no_hd='\0'; +} + +/* Suppress dash espace "- " of PGP/GPG signed message */ +/* In text part of a PGP/GPG signed message, all lines + * that begins with "-", are replaced with lines that + * begins "dash escape" "- ". + */ +static void suppress_dashescape(gchar * text_no_dash, gchar * text) +{ + gchar *index=NULL; + gchar dash_esc[]="- "; + + for (index=text; *index != '\0'; index++) { + if (strncmp(index,dash_esc,strlen(dash_esc))==0) { + index+=strlen(dash_esc); + while (*index != '\n' && *index != '\0') { + *text_no_dash=*index; + index++; + text_no_dash++; + } + if (*index=='\n') { + *text_no_dash='\n'; + text_no_dash++; + } + else { break; } + } + else { + *text_no_dash=*index; + text_no_dash++; + } + } + *text_no_dash='\0'; +} + +/* Replace all line endings with CRLF for a PGP/GPG signed message */ +static void string_crlf(gchar * new, gchar * string) +{ + while (*string != '\0') { + switch (*string) { + case '\n': + *new='\r'; + new++; + *new='\n'; + break; + default: + *new=*string; + break; + } + string++; + new++; + } + *new='\0'; +} + +static void +print_data(GpgmeData dh) +{ + char buf[100]; + size_t nread; + GpgmeError err; + + err = gpgme_data_rewind ( dh ); + fail_if_err (err); + while ( !(err = gpgme_data_read ( dh, buf, 100, &nread )) ) { + fwrite ( buf, nread, 1, stdout ); + } + if (err != GPGME_EOF) + fail_if_err (err); +} + +static void +read_data(char *data, GpgmeData dh) +{ + char buf[101]; + size_t nread; + GpgmeError err; + + err = gpgme_data_rewind ( dh ); + fail_if_err (err); + while ( !(err = gpgme_data_read ( dh, buf, 100, &nread )) ) { + buf[nread]='\0'; + strncat(data,buf,nread+1); + } + if (err != GPGME_EOF) + fail_if_err (err); +} + +/* Recup de SYLPHEED passphrase.c */ +static gint +linelen (const gchar *s) +{ + gint i; + + for (i = 0; *s && *s != '\n'; s++, i++); + + return i; +} + +/* Recup SYLPHEED passphrase.c */ +static gchar* +create_description (const gchar *desc) +{ + const gchar *cmd = NULL, *uid = NULL, *info = NULL; + gchar *prompt; + + cmd = desc; + uid = strchr (cmd, '\n'); + if (uid) { + info = strchr (++uid, '\n'); + if (info) info++; + } + + if (!uid) uid = _("[no user id]"); + if (!info) info = ""; + + /*prompt = g_strdup_printf (_("%sPlease enter the passphrase for:\n\n" + " %.*s \n" + "(%.*s)\n"), + !strncmp (cmd, "TRY_AGAIN", 9 ) ? + _("Bad passphrase! Try again...\n\n") : "", + linelen (uid), uid, linelen (info), info);*/ + prompt = g_strdup_printf ("%s%.*s", + !strncmp (cmd, "TRY_AGAIN", 9 ) ? _("Bad passphrase for key ! Try again...\n\n") : + _("Please enter the passphrase for key :\n\n"), + linelen (uid), uid); + + return (gchar*) prompt; +} + +static void +handle_password(gchar * string, gchar ** target) +{ + *target = string; +} + +static const char * +passphrase_cb(void *opaque, const char *desc, void **r_hd ) +{ + struct passphrase_cb_info_s *info = opaque; + GpgmeCtx ctx = info ? info->c : NULL; + GtkWidget* dialog; + const char *pass = NULL; + gchar *prompt; + + if (!desc) { + /* FIXME: cleanup by looking at *r_hd */ + return NULL; + } + + prompt = create_description(desc); + pass = get_passphrase(prompt); + g_free(prompt); + + if (!pass) { gpgme_cancel (ctx); } + + return (char*) pass; +} + +/* + * + * Public "libbalsa_gpg_" functions + * + */ + +/* Detect PGP/GPG signed message in body */ +gboolean libbalsa_gpg_sign_detect(gchar *msg) +{ + if (strstr (msg, "-----BEGIN PGP SIGNED MESSAGE-----\n")) { return TRUE; } + else { return FALSE; } +} + +/* Extract text and signature parts of a PGP/GPG signed message */ +void libbalsa_gpg_sign_extract(gchar *msg, gchar *text, gchar *sign) +{ + gchar *text_no_hd=NULL, *text_no_dash=NULL; + gchar *sign1=NULL, *text1=NULL; + + text1 = g_malloc0(sizeof(gchar)*strlen(msg)); + sign1 = g_malloc0(sizeof(gchar)*strlen(msg)); + + text_sign_extract(msg,text1,sign1); + + text_no_hd = g_malloc(sizeof(gchar)*strlen(text1)); + text_no_dash = g_malloc(sizeof(gchar)*strlen(text1)); + + /* Suppress GPG SIGNED MESSAGE headers of text part */ + suppress_msg_headers(text_no_hd,text1); + /* Suppress dashescape of text part */ + suppress_dashescape(text_no_dash,text_no_hd); + /* Convert line endings in CRLF */ + string_crlf(text,text_no_dash); + string_crlf(sign,sign1); + + g_free(text1); + g_free(sign1); + g_free(text_no_hd); + g_free(text_no_dash); +} + +/* Verify PGP/GPG signature for a pgp signed message */ +char* libbalsa_gpg_sign_verify(gchar *text, gchar *sign) +{ + GpgmeCtx ctx; + GpgmeError err; + GpgmeData sig, txt; + GpgmeSigStat status; + GtkWidget *d = NULL; + char *result; + + err = gpgme_new (&ctx); + fail_if_err (err); + + err = gpgme_data_new_from_mem (&txt,text,strlen(text),0); + fail_if_err (err); + err = gpgme_data_new_from_mem (&sig,sign,strlen(sign),0); + fail_if_err (err); + + err = gpgme_op_verify (ctx,sig,txt,&status); + fail_if_err (err); + result = sig_status_full(ctx,status); + + gpgme_data_release (sig); + gpgme_data_release (txt); + gpgme_release (ctx); + + return result; +} + +/* Sign a message with a cleartext signature */ +void libbalsa_gpg_sign_message (char * sign_message, char * message) +{ + GpgmeCtx ctx; + GpgmeError err; + GpgmeData in, out; + char *data; + struct passphrase_cb_info_s info; + + memset (&info, 0, sizeof info); + + err = gpgme_new (&ctx); + fail_if_err (err); + if ( !getenv("GPG_AGENT_INFO") ) { + info.c = ctx; + gpgme_set_passphrase_cb(ctx,passphrase_cb,&info); + } + + gpgme_set_textmode (ctx, 1); + gpgme_set_armor (ctx, 1); + + err = gpgme_data_new_from_mem ( &in, message, strlen(message), 0 ); + fail_if_err (err); + + /* Sign with cleartext signature */ + err = gpgme_data_new ( &out ); + fail_if_err (err); + err = gpgme_op_sign (ctx,in,out,GPGME_SIG_MODE_CLEAR); + fail_if_err (err); + + read_data(sign_message,out); + + gpgme_data_release(out); + gpgme_data_rewind(in); + gpgme_data_release(in); + gpgme_release(ctx); +} + +#endif /* USE_GPME */ diff -ruN balsa-1.3.5/libbalsa/gpg.h balsa-1.3.5-patch/libbalsa/gpg.h --- balsa-1.3.5/libbalsa/gpg.h Thu Jan 1 01:00:00 1970 +++ balsa-1.3.5-patch/libbalsa/gpg.h Thu Apr 25 18:59:03 2002 @@ -0,0 +1,44 @@ +/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */ +/* Balsa E-Mail Client + * Copyright (C) 1997-2001 Stuart Parmenter and others, + * See the file AUTHORS for a list. + * + * 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, or (at your option) + * any later version. + * + * 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. + */ + +/* PGP/GPG support with GPGME library + * Laurent Cheylus - + */ + +#ifndef __LIBBALSA_GPG_H__ +#define __LIBBALSA_GPG_H__ + +/* Sendmessage window for parenting */ +GtkWidget *sendmsg_window; + +/* Key Type for signing */ +enum +{ + DEFAULT_KEY, + EMAIL_ADDRESS_KEY +}; + +void libbalsa_gpg_sign_message(char*, char*); +gboolean libbalsa_gpg_sign_detect(gchar*); +void libbalsa_gpg_sign_extract(gchar*, gchar*, gchar*); +char* libbalsa_gpg_sign_verify(gchar*, gchar*); + +#endif /* __LIBBALSA_GPG_H__ */ diff -ruN balsa-1.3.5/libbalsa/libbalsa.h balsa-1.3.5-patch/libbalsa/libbalsa.h --- balsa-1.3.5/libbalsa/libbalsa.h Wed Apr 24 11:36:04 2002 +++ balsa-1.3.5-patch/libbalsa/libbalsa.h Thu Apr 25 18:58:43 2002 @@ -53,6 +53,10 @@ #include "address-book-ldap.h" #endif +#if USE_GPGME +#include "gpg.h" +#endif + #include "mailbox.h" #include "mailbox_local.h" #include "mailbox_remote.h" diff -ruN balsa-1.3.5/src/balsa-app.c balsa-1.3.5-patch/src/balsa-app.c --- balsa-1.3.5/src/balsa-app.c Tue Apr 16 00:12:58 2002 +++ balsa-1.3.5-patch/src/balsa-app.c Thu Apr 25 19:00:49 2002 @@ -410,6 +410,12 @@ /* Message filing */ balsa_app.folder_mru=NULL; balsa_app.drag_default_is_move=0; + +#if USE_GPGME + balsa_app.sign_style = 0; + /* OpenPGP verification */ + balsa_app.verify_gpg_signed_message=0; +#endif } gboolean diff -ruN balsa-1.3.5/src/balsa-app.h balsa-1.3.5-patch/src/balsa-app.h --- balsa-1.3.5/src/balsa-app.h Thu Feb 21 12:00:36 2002 +++ balsa-1.3.5-patch/src/balsa-app.h Thu Apr 25 19:00:45 2002 @@ -357,6 +357,11 @@ gboolean delete_immediately; gboolean hide_deleted; +#if USE_GPGME + guint sign_style; + gint verify_gpg_signed_message; +#endif + } balsa_app; #define BALSA_IS_MAILBOX_SPECIAL(a) ((a)==balsa_app.inbox || (a)==balsa_app.trash || (a)==balsa_app.outbox||(a)==balsa_app.draftbox) diff -ruN balsa-1.3.5/src/balsa-message.c balsa-1.3.5-patch/src/balsa-message.c --- balsa-1.3.5/src/balsa-message.c Tue Mar 19 10:23:59 2002 +++ balsa-1.3.5-patch/src/balsa-message.c Thu Apr 25 19:00:19 2002 @@ -52,6 +52,10 @@ # include #endif +#ifdef USE_GPGME +#include "gpg.h" +#endif + #include "quote-color.h" #include "sendmsg-window.h" @@ -1836,6 +1840,10 @@ size_t alloced; gchar **l = NULL, **lines = NULL, *line = NULL; gint quote_level = 0; +#ifdef USE_GPGME + gchar *sign=NULL, *text=NULL, *status=NULL; + GtkWidget *d = NULL; +#endif /* one-time compilation of a constant url_str expression */ if (!url_reg) { @@ -1865,6 +1873,26 @@ alloced = libbalsa_readfile(fp, &ptr); if (!ptr) return; +#ifdef USE_GPGME + /* Verify PGP SIGNED MESSAGE */ + if ((balsa_app.verify_gpg_signed_message == 1) && libbalsa_gpg_sign_detect(ptr)) { + text = g_malloc(sizeof(gchar)*strlen(ptr)); + sign = g_malloc(sizeof(gchar)*strlen(ptr)); + + /* Extract cleartext and signature from body */ + libbalsa_gpg_sign_extract(ptr,text,sign); + + /* Verify PGP/GPG text with sign */ + status = libbalsa_gpg_sign_verify(text,sign); + + d = gnome_ok_dialog_parented(status,GTK_WINDOW(balsa_app.main_window)); + gnome_dialog_run(GNOME_DIALOG(d)); + + g_free(text); + g_free(sign); + } +#endif + content_type = libbalsa_message_body_get_content_type(info->body); ishtml = (g_strcasecmp(content_type, "text/html") == 0); g_free(content_type); diff -ruN balsa-1.3.5/src/pref-manager.c balsa-1.3.5-patch/src/pref-manager.c --- balsa-1.3.5/src/pref-manager.c Fri Feb 22 22:14:19 2002 +++ balsa-1.3.5-patch/src/pref-manager.c Thu Apr 25 19:00:56 2002 @@ -47,6 +47,11 @@ #define NUM_PWINDOW_MODES 3 #define NUM_THREADING_STYLES 3 +#if USE_GPGME +#define NUM_SIGN_MODES 2 +#include "../libbalsa/gpg.h" +#endif + typedef struct _PropertyUI { GtkRadioButton *toolbar_type[NUM_TOOLBAR_MODES]; @@ -68,6 +73,10 @@ GtkWidget *check_imap; GtkWidget *check_imap_inbox; GtkWidget *notify_new_mail_dialog; +#if USE_GPGME + GtkWidget *verify_gpg_signed_message; + GtkRadioButton *sign_type[NUM_SIGN_MODES]; +#endif GtkWidget *mdn_reply_clean_menu, *mdn_reply_notclean_menu; GtkWidget *close_mailbox_auto; @@ -178,6 +187,9 @@ static GtkWidget *incoming_page(gpointer); static GtkWidget *outgoing_page(gpointer); +#ifdef USE_GPGME +static GtkWidget *privacy_page(gpointer); +#endif static void destroy_pref_window_cb(GtkWidget * pbox, gpointer data); static void set_prefs(void); static void apply_prefs(GnomePropertyBox * pbox, gint page_num); @@ -233,6 +245,18 @@ N_("quoted") }; +#if USE_GPGME +gchar *sign_type_label[NUM_SIGN_MODES] = { + N_("Default key"), + N_("Select key by your email address"), +}; + +guint sign_type[NUM_SIGN_MODES] = { + DEFAULT_KEY, + EMAIL_ADDRESS_KEY +}; +#endif + guint pwindow_type[NUM_PWINDOW_MODES] = { WHILERETR, UNTILCLOSED, @@ -437,6 +461,15 @@ GTK_SIGNAL_FUNC(properties_modified_cb), property_box); gtk_signal_connect(GTK_OBJECT(pui->forward_attached), "toggled", GTK_SIGNAL_FUNC(properties_modified_cb), property_box); +#if USE_GPGME + gtk_signal_connect(GTK_OBJECT(pui->verify_gpg_signed_message), "toggled", + GTK_SIGNAL_FUNC(properties_modified_cb), property_box); + + for (i = 0; i < NUM_SIGN_MODES; i++) { + gtk_signal_connect(GTK_OBJECT(pui->sign_type[i]), "clicked", + properties_modified_cb, property_box); + } +#endif /* external editor */ gtk_signal_connect(GTK_OBJECT(pui->extern_editor_command), "changed", @@ -717,8 +750,18 @@ balsa_index_hide_deleted(hide); } } +#if USE_GPGME + balsa_app.verify_gpg_signed_message = + GTK_TOGGLE_BUTTON(pui->verify_gpg_signed_message)->active; + + for (i = 0; i < NUM_SIGN_MODES; i++) + if (GTK_TOGGLE_BUTTON(pui->sign_type[i])->active) { + balsa_app.sign_style = sign_type[i]; + break; + } +#endif - /* external editor */ + /* external editor */ g_free(balsa_app.extern_editor_command); balsa_app.extern_editor_command = g_strdup(gtk_entry_get_text(GTK_ENTRY(pui->extern_editor_command))); @@ -1002,7 +1045,10 @@ GTK_TOGGLE_BUTTON(pui->wordwrap)->active); gtk_widget_set_sensitive(pui->send_rfc2646_format_flowed, GTK_TOGGLE_BUTTON(pui->wordwrap)->active); - +#if USE_GPGME + gtk_toggle_button_set_active( + GTK_TOGGLE_BUTTON(pui->verify_gpg_signed_message), balsa_app.verify_gpg_signed_message); +#endif /* external editor */ gtk_entry_set_text(GTK_ENTRY(pui->extern_editor_command), balsa_app.extern_editor_command); @@ -1521,9 +1567,58 @@ gtk_label_new(_("Incoming"))); gtk_notebook_append_page(GTK_NOTEBOOK(note), outgoing_page(data), gtk_label_new(_("Outgoing"))); +#ifdef USE_GPGME + gtk_notebook_append_page(GTK_NOTEBOOK(note), privacy_page(data), + gtk_label_new(_("Privacy"))); +#endif return note; } + +#ifdef USE_GPGME +static GtkWidget * +privacy_page(gpointer data) +{ + GtkWidget *vbox1; + GtkWidget *vbox2; + GtkWidget *frame1; + GtkWidget *frame2; + GSList *group; + gint i; + + vbox1 = gtk_vbox_new(FALSE, 0); + + frame1 = gtk_frame_new(_("Verify")); + gtk_container_set_border_width(GTK_CONTAINER(frame1), 5); + gtk_box_pack_start(GTK_BOX(vbox1), frame1, FALSE, FALSE, 0); + + vbox2 = vbox_in_container(frame1); + + pui->verify_gpg_signed_message = gtk_check_button_new_with_label( + _("Always verify GPG signed message")); + gtk_box_pack_start(GTK_BOX(vbox2), pui->verify_gpg_signed_message, + FALSE, FALSE, 0); + + frame2 = gtk_frame_new(_("Sign")); + gtk_box_pack_start(GTK_BOX(vbox1), frame2, FALSE, FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(frame2), 5); + + vbox2 = vbox_in_container(frame2); + + group = NULL; + for (i = 0; i < NUM_SIGN_MODES; i++) { + pui->sign_type[i] = + GTK_RADIO_BUTTON(gtk_radio_button_new_with_label + (group, _(sign_type_label[i]))); + gtk_box_pack_start(GTK_BOX(vbox2), + GTK_WIDGET(pui->sign_type[i]), FALSE, TRUE, + 0); + group = gtk_radio_button_group(pui->sign_type[i]); + } + + return vbox1; +} +#endif static GtkWidget * incoming_page(gpointer data) diff -ruN balsa-1.3.5/src/save-restore.c balsa-1.3.5-patch/src/save-restore.c --- balsa-1.3.5/src/save-restore.c Wed Apr 24 11:36:04 2002 +++ balsa-1.3.5-patch/src/save-restore.c Thu Apr 25 19:01:07 2002 @@ -603,6 +603,10 @@ if (balsa_app.browse_wrap_length < 40) balsa_app.browse_wrap_length = 40; +#if USE_GPGME + balsa_app.verify_gpg_signed_message = gnome_config_get_bool("VerifySigned=false"); +#endif + gnome_config_pop_prefix(); /* Interface Options ... */ @@ -766,6 +770,10 @@ balsa_app.always_queue_sent_mail = d_get_gint("AlwaysQueueSentMail", 0); balsa_app.copy_to_sentbox = d_get_gint("CopyToSentbox", 1); +#if USE_GPGME + balsa_app.sign_style = gnome_config_get_int("SignStyle=0"); +#endif + gnome_config_pop_prefix(); /* Compose window ... */ @@ -922,6 +930,9 @@ save_color("UrlColor", &balsa_app.url_color); save_color("BadAddressColor", &balsa_app.bad_address_color); +#if USE_GPGME + gnome_config_set_int("VerifySigned", balsa_app.verify_gpg_signed_message); +#endif gnome_config_pop_prefix(); @@ -1031,6 +1042,10 @@ gnome_config_set_int("AlwaysQueueSentMail", balsa_app.always_queue_sent_mail); gnome_config_set_int("CopyToSentbox", balsa_app.copy_to_sentbox); +#if USE_GPGME + gnome_config_set_int("SignStyle", balsa_app.sign_style); +#endif + gnome_config_pop_prefix(); /* Compose window ... */ diff -ruN balsa-1.3.5/src/sendmsg-window.c balsa-1.3.5-patch/src/sendmsg-window.c --- balsa-1.3.5/src/sendmsg-window.c Tue Apr 23 13:31:43 2002 +++ balsa-1.3.5-patch/src/sendmsg-window.c Thu Apr 25 19:00:29 2002 @@ -67,6 +67,10 @@ #include "threads.h" #endif +#ifdef USE_GPGME +#include "gpg.h" +#endif + #include "sendmsg-window.h" #include "address-book.h" #include "address-entry.h" @@ -119,7 +123,10 @@ static gint toggle_keywords_cb(GtkWidget *, BalsaSendmsg *); static gint toggle_reqdispnotify_cb(GtkWidget * widget, BalsaSendmsg * bsmsg); static gint toggle_queue_cb(GtkWidget * widget, BalsaSendmsg * bsmsg); - +#ifdef USE_GPGME +static gint toggle_sign_cb(GtkWidget * widget, BalsaSendmsg * bsmsg); +static void handle_password(gchar * string, gchar ** target); +#endif static void spell_check_cb(GtkWidget * widget, BalsaSendmsg *); static void spell_check_done_cb(BalsaSpellCheck * spell_check, BalsaSendmsg *); @@ -409,7 +416,11 @@ toggle_reqdispnotify_cb, NULL), GNOMEUIINFO_TOGGLEITEM(N_("_Always Queue Sent Mail"), NULL, toggle_queue_cb, NULL), - GNOMEUIINFO_END +#ifdef USE_GPGME + GNOMEUIINFO_TOGGLEITEM(N_("_Sign message with PGP/GPG"), NULL, + toggle_sign_cb, NULL), +#endif + GNOMEUIINFO_END }; #define CASE_INSENSITIVE_NAME @@ -528,6 +539,9 @@ static gint mail_headers_page; static gint spell_check_page; +#ifdef USE_GPGME +static gboolean msg_sign=FALSE; +#endif /* the callback handlers */ static void @@ -3158,6 +3172,29 @@ *ins = '\0'; } +#ifdef USE_GPGME + +/* Dialog for password request */ +static void +handle_password(gchar * string, gchar ** target) +{ + *target = string; +} + +gchar* +get_passphrase(gchar* prompt) +{ + GtkWidget* dialog; + gchar *pass = NULL; + + dialog = gnome_request_dialog(TRUE,prompt,NULL,0,(GnomeStringCallback) handle_password, + (gpointer) &pass, GTK_WINDOW(sendmsg_window)); + gnome_dialog_run_and_close(GNOME_DIALOG(dialog)); + + return pass; +} +#endif + /* bsmsg2message: creates Message struct based on given BalsaMessage stripping EOL chars is necessary - the GtkEntry fields can in principle @@ -3170,8 +3207,10 @@ LibBalsaMessage *message; LibBalsaMessageBody *body; GList *list; - gchar *tmp; - gchar recvtime[50]; + gchar *tmp; + gchar *buffer_tmp; + gchar *sign_buffer; + gchar recvtime[50]; struct tm *footime; g_assert(bsmsg != NULL); @@ -3180,7 +3219,7 @@ tmp = libbalsa_address_entry_get_chars_all(LIBBALSA_ADDRESS_ENTRY (bsmsg->from[1])); - message->from = libbalsa_address_new_from_string(tmp); + message->from = libbalsa_address_new_from_string(tmp); g_free(tmp); tmp = gtk_editable_get_chars(GTK_EDITABLE(bsmsg->subject[1]), 0, -1); @@ -3190,7 +3229,7 @@ tmp = libbalsa_address_entry_get_chars_all(LIBBALSA_ADDRESS_ENTRY (bsmsg->to[1])); - message->to_list = libbalsa_address_new_list_from_string(tmp); + message->to_list = libbalsa_address_new_list_from_string(tmp); g_free(tmp); tmp = @@ -3260,17 +3299,31 @@ body = libbalsa_message_body_new(message); body->disposition = DISPINLINE; /* this is the main body */ - body->buffer = gtk_editable_get_chars(GTK_EDITABLE(bsmsg->text), 0, + buffer_tmp = gtk_editable_get_chars(GTK_EDITABLE(bsmsg->text), 0, gtk_text_get_length(GTK_TEXT (bsmsg->text))); if (bsmsg->flow) { - body->buffer = - libbalsa_wrap_rfc2646(body->buffer, balsa_app.wraplength, TRUE, + buffer_tmp = + libbalsa_wrap_rfc2646(buffer_tmp, balsa_app.wraplength, TRUE, FALSE); } else if (balsa_app.wordwrap) - libbalsa_wrap_string(body->buffer, balsa_app.wraplength); + libbalsa_wrap_string(buffer_tmp, balsa_app.wraplength); body->charset = g_strdup(bsmsg->charset); - libbalsa_message_append_part(message, body); + +#ifdef USE_GPGME + /* Sign message with PGP/GPG */ + body->buffer=g_malloc0(sizeof(gchar)*(strlen(buffer_tmp)+8*1024)); + + if (msg_sign) { + sendmsg_window = (GtkWidget*) bsmsg->window; + libbalsa_gpg_sign_message(body->buffer,buffer_tmp); + } + else { strcpy(body->buffer,buffer_tmp); } +#else + strcpy(body->buffer,buffer_tmp); +#endif + + libbalsa_message_append_part(message, body); { /* handle attachments */ gint i; @@ -3813,6 +3866,15 @@ balsa_app.always_queue_sent_mail = GTK_CHECK_MENU_ITEM(widget)->active; return TRUE; } + +#ifdef USE_GPGME +static gint +toggle_sign_cb(GtkWidget * widget, BalsaSendmsg * bsmsg) +{ + msg_sign = GTK_CHECK_MENU_ITEM(widget)->active; + return TRUE; +} +#endif /* init_menus: performs the initial menu setup: shown headers as well as correct diff -ruN balsa-1.3.5/src/sendmsg-window.h balsa-1.3.5-patch/src/sendmsg-window.h --- balsa-1.3.5/src/sendmsg-window.h Sun Apr 14 21:28:38 2002 +++ balsa-1.3.5-patch/src/sendmsg-window.h Thu Apr 25 19:00:32 2002 @@ -101,6 +101,10 @@ BalsaSendmsg *sendmsg_window_new_from_list(GtkWidget * w, GList * message_list, SendType type); +#ifdef USE_GPGME + gchar* get_passphrase(gchar * prompt); +#endif + #define SENDMSG_WINDOW_QUIT_ON_CLOSE(bsmsg) ((bsmsg)->quit_on_close=TRUE) #ifdef __cplusplus