<<< Date Index >>>     <<< Thread Index >>>

Re: What should go into 1.5.7?



On Jan 26, Thomas Roessler <roessler@xxxxxxxxxxxxxxxxxx> wrote:

> If there are any patches you want to see included with 1.5.7, please
> re-submit them to mutt-dev by Thursday evening.  1.5.7 due on Friday.
Here I'm attaching two patches. The first adds support for gnutls.
I have been using it for the debian package since november 2001 and it
has been solid for a long time. Please, please please apply it, because
it's the only way for a distribution to support SSL, due to the well
known OpenSSL/GPL licensing incompatibility.
Also, gnutls has a much nicer interface... :-)

The other patch is a subset of the ja patch, it adds the the config
options assumed_charset, file_charset and strict_mime. I can't see how
people using mutt in the real world and having to deal with Outlook
users can live without it.

-- 
ciao,
Marco
# vi: ft=diff
This is the gnutls patch by Andrew McDonald <andrew@xxxxxxxxxxxxxxx>.

The home page for this patch is:

  http://www.mcdonald.org.uk/andrew/mutt-gnutls/

* Patch last synced with upstream:
  - Date: unknown (mutt_1.5.6-1)
  - File: 
http://www.mcdonald.org.uk/andrew/mutt-gnutls/002_patch-1.5.5.1.admcd.gnutls.59

* Changes made:
  - 2004-07-22: updated globals.h diff to get it to apply cleanly to
    current CVS.
  - 2004-09-16: updated globals.h and mutt_sasl.c to get it to apply
    cleanly to current CVS.

== END PATCH
diff -Nru mutt-1.5.5.1.orig/PATCHES mutt-1.5.5.1/PATCHES
--- mutt-1.5.5.1.orig/PATCHES   2004-04-04 21:07:55.000000000 +0100
+++ mutt-1.5.5.1/PATCHES        2004-04-04 21:08:34.000000000 +0100
@@ -1,0 +1 @@
+patch-1.4.admcd.gnutls.59d
diff -Nru mutt-1.5.5.1.orig/globals.h mutt-1.5.5.1/globals.h
--- mutt-1.5.5.1.orig/globals.h 2003-11-05 09:41:31.000000000 +0000
+++ mutt-1.5.5.1/globals.h      2004-04-04 21:08:34.000000000 +0100
@@ -102,12 +102,18 @@
 WHERE char *Signature;
 WHERE char *SimpleSearch;
 WHERE char *Spoolfile;
 WHERE char *SpamSep;
-#if defined(USE_SSL) || defined(USE_NSS)
+#if defined(USE_SSL) || defined(USE_NSS) || defined(USE_GNUTLS)
 WHERE char *SslCertFile INITVAL (NULL);
+#endif
+#if defined(USE_SSL) || defined(USE_NSS)
 WHERE char *SslEntropyFile INITVAL (NULL);
 WHERE char *SslClientCert INITVAL (NULL);
 #endif
+#ifdef USE_GNUTLS
+WHERE short SslDHPrimeBits;
+WHERE char *SslCACertFile INITVAL (NULL);
+#endif
 WHERE char *StChars;
 WHERE char *Status;
 WHERE char *Tempdir;
diff -Nru mutt-1.5.5.1.orig/imap/imap.c mutt-1.5.5.1/imap/imap.c
--- mutt-1.5.5.1.orig/imap/imap.c       2004-04-04 21:07:54.000000000 +0100
+++ mutt-1.5.5.1/imap/imap.c    2004-04-04 21:08:34.000000000 +0100
@@ -29,7 +29,7 @@
 #include "browser.h"
 #include "message.h"
 #include "imap_private.h"
-#ifdef USE_SSL
+#if defined(USE_SSL) || defined(USE_GNUTLS)
 # include "mutt_ssl.h"
 #endif
 
@@ -408,7 +408,7 @@
     /* TODO: Parse new tagged CAPABILITY data (* OK [CAPABILITY...]) */
     if (imap_check_capabilities (idata))
       goto bail;
-#if defined(USE_SSL) && !defined(USE_NSS)
+#if defined(USE_SSL) || defined(USE_GNUTLS)
     /* Attempt STARTTLS if available and desired. */
     if (mutt_bit_isset (idata->capabilities, STARTTLS) && !idata->conn->ssf)
     {
@@ -422,7 +422,11 @@
          goto bail;
        if (rc != -2)
        {
+#ifdef USE_SSL
          if (mutt_ssl_starttls (idata->conn))
+#elif USE_GNUTLS
+         if (mutt_gnutls_starttls (idata->conn))
+#endif
          {
            mutt_error (_("Could not negotiate TLS connection"));
            mutt_sleep (1);
diff -Nru mutt-1.5.5.1.orig/imap/util.c mutt-1.5.5.1/imap/util.c
--- mutt-1.5.5.1.orig/imap/util.c       2003-11-05 09:41:36.000000000 +0000
+++ mutt-1.5.5.1/imap/util.c    2004-04-04 21:08:34.000000000 +0100
@@ -164,7 +164,7 @@
     }
   }
   
-#ifdef USE_SSL
+#if defined(USE_SSL) || defined(USE_GNUTLS)
   if (option (OPTIMAPFORCESSL))
     mx->account.flags |= M_ACCT_SSL;
 #endif
diff -Nru mutt-1.5.5.1.orig/init.c mutt-1.5.5.1/init.c
--- mutt-1.5.5.1.orig/init.c    2003-11-05 09:41:31.000000000 +0000
+++ mutt-1.5.5.1/init.c 2004-04-04 21:08:34.000000000 +0100
@@ -27,7 +27,7 @@
 #include "mutt_crypt.h"
 #include "mutt_idna.h"
 
-#if defined(USE_SSL) || defined(USE_NSS)
+#if defined(USE_SSL) || defined(USE_NSS) || defined(USE_GNUTLS)
 #include "mutt_ssl.h"
 #endif
 
diff -Nru mutt-1.5.5.1.orig/init.h mutt-1.5.5.1/init.h
--- mutt-1.5.5.1.orig/init.h    2004-04-04 21:07:55.000000000 +0100
+++ mutt-1.5.5.1/init.h 2004-04-04 21:08:34.000000000 +0100
@@ -782,7 +782,7 @@
   ** as folder separators for displaying IMAP paths. In particular it
   ** helps in using the '=' shortcut for your \fIfolder\fP variable.
   */
-# ifdef USE_SSL
+# if defined(USE_SSL) || defined(USE_GNUTLS)
   { "imap_force_ssl",          DT_BOOL, R_NONE, OPTIMAPFORCESSL, 0 },
   /*
   ** .pp
@@ -1737,8 +1737,8 @@
   ** (S/MIME only)
   */
   
-#if defined(USE_SSL)||defined(USE_NSS)
-# ifndef USE_NSS  
+#if defined(USE_SSL)||defined(USE_NSS)||defined(USE_GNUTLS)
+# if defined(USE_SSL)||defined(USE_GNUTLS)
   { "ssl_starttls", DT_QUAD, R_NONE, OPT_SSLSTARTTLS, M_YES },
   /*
   ** .pp
@@ -1762,6 +1762,7 @@
   ** .pp
   ** Example: set certificate_file=~/.mutt/certificates
   */
+# ifndef USE_GNUTLS
   { "ssl_usesystemcerts", DT_BOOL, R_NONE, OPTSSLSYSTEMCERTS, 1 },
   /*
   ** .pp
@@ -1781,6 +1782,7 @@
   ** This variables specifies whether to attempt to use SSLv2 in the
   ** SSL authentication process.
   */
+# endif
   { "ssl_use_sslv3", DT_BOOL, R_NONE, OPTSSLV3, 1 },
   /*
   ** .pp
@@ -1793,6 +1795,24 @@
   ** This variables specifies whether to attempt to use TLSv1 in the
   ** SSL authentication process.
   */
+#ifdef USE_GNUTLS
+  { "ssl_min_dh_prime_bits", DT_NUM, R_NONE, UL &SslDHPrimeBits, 0 },
+  /*
+  ** .pp
+  ** This variable specifies the minimum acceptable prime size (in bits)
+  ** for use in any Diffie-Hellman key exchange. A value of 0 will use
+  ** the default from the GNUTLS library.
+  */
+  { "ssl_ca_certificates_file", DT_PATH, R_NONE, UL &SslCACertFile, 0 },
+  /*
+  ** .pp
+  ** This variable specifies a file containing trusted CA certificates.
+  ** Any server certificate that is signed with one of these CA
+  ** certificates are also automatically accepted.
+  ** .pp
+  ** Example: set ssl_ca_certificates_file=/etc/ssl/certs/ca-certificates.crt
+  */
+#endif
 #endif
 
   { "pipe_split",      DT_BOOL, R_NONE, OPTPIPESPLIT, 0 },
diff -Nru mutt-1.5.5.1.orig/main.c mutt-1.5.5.1/main.c
--- mutt-1.5.5.1.orig/main.c    2004-04-04 21:07:55.000000000 +0100
+++ mutt-1.5.5.1/main.c 2004-04-04 21:08:34.000000000 +0100
@@ -241,6 +241,12 @@
        "-USE_SSL  "
 #endif
 
+#ifdef USE_GNUTLS
+       "+USE_GNUTLS  "
+#else
+       "-USE_GNUTLS  "
+#endif
+
 #ifdef USE_SASL
        "+USE_SASL  "
 #else
diff -Nru mutt-1.5.5.1.orig/mutt.h mutt-1.5.5.1/mutt.h
--- mutt-1.5.5.1.orig/mutt.h    2004-04-04 21:07:55.000000000 +0100
+++ mutt-1.5.5.1/mutt.h 2004-04-04 21:08:34.000000000 +0100
@@ -289,7 +289,7 @@
   OPT_QUIT,
   OPT_REPLYTO,
   OPT_RECALL,
-#ifdef USE_SSL
+#if defined(USE_SSL) || defined(USE_GNUTLS)
   OPT_SSLSTARTTLS,
 #endif
   OPT_SUBJECT,
@@ -362,15 +362,19 @@
   OPTIMAPPASSIVE,
   OPTIMAPPEEK,
   OPTIMAPSERVERNOISE,
-# ifdef USE_SSL
+# if defined(USE_SSL) || defined(USE_GNUTLS)
   OPTIMAPFORCESSL,
 # endif
 #endif
-#if defined(USE_SSL) || defined(USE_NSS)
+#if defined(USE_SSL) || defined(USE_NSS) || defined(USE_GNUTLS)
+# ifndef USE_GNUTLS
   OPTSSLV2,
+# endif
   OPTSSLV3,
   OPTTLSV1,
+# ifndef USE_GNUTLS
   OPTSSLSYSTEMCERTS,
+# endif
 #endif
   OPTIMPLICITAUTOVIEW,
   OPTKEEPFLAGGED,
diff -Nru mutt-1.5.5.1.orig/mutt_sasl.c mutt-1.5.5.1/mutt_sasl.c
--- mutt-1.5.5.1.orig/mutt_sasl.c       2004-04-04 21:07:54.000000000 +0100
+++ mutt-1.5.5.1/mutt_sasl.c    2004-04-04 21:08:34.000000000 +0100
@@ -293,7 +293,7 @@
    * If someone does it'd probably be trivial to write mutt_nss_get_ssf().
    * I have a feeling more SSL code could be shared between those two files,
    * but I haven't looked into it yet, since I still don't know the APIs. */
-#if defined(USE_SSL)
+#if defined(USE_SSL) || defined(USE_GNUTLS)
   if (conn->ssf)
   {
 #ifdef USE_SASL2 /* I'm not sure this actually has an effect, at least with 
SASLv2 */
diff -Nru mutt-1.5.5.1.orig/mutt_socket.c mutt-1.5.5.1/mutt_socket.c
--- mutt-1.5.5.1.orig/mutt_socket.c     2004-04-04 21:07:54.000000000 +0100
+++ mutt-1.5.5.1/mutt_socket.c  2004-04-04 21:08:34.000000000 +0100
@@ -22,7 +22,7 @@
 #include "globals.h"
 #include "mutt_socket.h"
 #include "mutt_tunnel.h"
-#ifdef USE_SSL
+#if defined(USE_SSL) || defined(USE_GNUTLS) || defined(USE_NSS)
 # include "mutt_ssl.h"
 #endif
 
@@ -257,6 +257,12 @@
     ssl_socket_setup (conn);
 #elif USE_NSS
     mutt_nss_socket_setup (conn);
+#elif USE_GNUTLS
+    if (mutt_gnutls_socket_setup (conn) < 0)
+    {
+      mutt_socket_free (conn);
+      return NULL;
+    }
 #else
     mutt_error _("SSL is unavailable.");
     mutt_sleep (2);
diff -Nru mutt-1.5.5.1.orig/mutt_ssl.h mutt-1.5.5.1/mutt_ssl.h
--- mutt-1.5.5.1.orig/mutt_ssl.h        2002-01-24 12:10:50.000000000 +0000
+++ mutt-1.5.5.1/mutt_ssl.h     2004-04-04 21:08:34.000000000 +0100
@@ -21,8 +21,17 @@
 
 #include "mutt_socket.h"
 
+#ifdef USE_SSL
 int mutt_ssl_starttls (CONNECTION* conn);
 
 extern int ssl_socket_setup (CONNECTION *conn);
+#endif
+#ifdef USE_GNUTLS
+int mutt_gnutls_starttls (CONNECTION* conn);
 
+extern int mutt_gnutls_socket_setup (CONNECTION *conn);
+#endif
+#ifdef USE_NSS
+int mutt_nss_socket_setup (CONNECTION* conn);
+#endif
 #endif /* _MUTT_SSL_H_ */
diff -Nru mutt-1.5.5.1.orig/mutt_ssl_gnutls.c mutt-1.5.5.1/mutt_ssl_gnutls.c
--- mutt-1.5.5.1.orig/mutt_ssl_gnutls.c 1970-01-01 01:00:00.000000000 +0100
+++ mutt-1.5.5.1/mutt_ssl_gnutls.c      2004-04-04 21:08:41.000000000 +0100
@@ -0,0 +1,825 @@
+/* Copyright (C) 2001 Marco d'Itri <md@xxxxxxxx>
+ * Copyright (C) 2001-2004 Andrew McDonald <andrew@xxxxxxxxxxxxxxx>
+ *
+ *     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
+ *     (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, USA.
+ */
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#include "mutt.h"
+#include "mutt_socket.h"
+#include "mutt_curses.h"
+#include "mutt_menu.h"
+#include "mutt_ssl.h"
+#include "mutt_regex.h"
+
+typedef struct _tlssockdata
+{
+  gnutls_session state;
+  gnutls_certificate_credentials xcred;
+}
+tlssockdata;
+
+/* local prototypes */
+static int tls_socket_read (CONNECTION* conn, char* buf, size_t len);
+static int tls_socket_write (CONNECTION* conn, const char* buf, size_t len);
+static int tls_socket_open (CONNECTION* conn);
+static int tls_socket_close (CONNECTION* conn);
+static int tls_starttls_close (CONNECTION* conn);
+
+static int tls_init (void);
+static int tls_negotiate (CONNECTION* conn);
+static int tls_check_certificate (CONNECTION* conn);
+
+
+static int tls_init (void)
+{
+  static unsigned char init_complete = 0;
+  int err;
+
+  if (init_complete)
+    return 0;
+
+  err = gnutls_global_init();
+  if (err < 0)
+  {
+    mutt_error ("gnutls_global_init: %s", gnutls_strerror(err));
+    mutt_sleep (2);
+    return -1;
+  }
+
+  init_complete = 1;
+  return 0;
+}
+
+int mutt_gnutls_socket_setup (CONNECTION* conn)
+{
+  if (tls_init() < 0)
+    return -1;
+
+  conn->open   = tls_socket_open;
+  conn->read   = tls_socket_read;
+  conn->write  = tls_socket_write;
+  conn->close  = tls_socket_close;
+
+  return 0;
+}
+
+static int tls_socket_read (CONNECTION* conn, char* buf, size_t len)
+{
+  tlssockdata *data = conn->sockdata;
+  int ret;
+
+  if (!data)
+  {
+    mutt_error ("Error: no TLS socket open");
+    mutt_sleep (2);
+    return -1;
+  }
+
+  ret = gnutls_record_recv (data->state, buf, len);
+  if (gnutls_error_is_fatal(ret) == 1)
+  {
+    mutt_error ("tls_socket_read (%s)", gnutls_strerror (ret));
+    mutt_sleep (4);
+    return -1;
+  }
+  return ret;
+}
+
+static int tls_socket_write (CONNECTION* conn, const char* buf, size_t len)
+{
+  tlssockdata *data = conn->sockdata;
+  int ret;
+
+  if (!data)
+  {
+    mutt_error ("Error: no TLS socket open");
+    mutt_sleep (2);
+    return -1;
+  }
+
+  ret = gnutls_record_send (data->state, buf, len);
+  if (gnutls_error_is_fatal(ret) == 1)
+  {
+    mutt_error ("tls_socket_write (%s)", gnutls_strerror (ret));
+    mutt_sleep (4);
+    return -1;
+  }
+  return ret;
+}
+
+static int tls_socket_open (CONNECTION* conn)
+{
+  if (raw_socket_open (conn) < 0)
+    return -1;
+
+  if (tls_negotiate (conn) < 0)
+  {
+    tls_socket_close (conn);
+    return -1;
+  }
+
+  return 0;
+}
+
+int mutt_gnutls_starttls (CONNECTION* conn)
+{
+  if (tls_init() < 0)
+    return -1;
+
+  if (tls_negotiate (conn) < 0)
+    return -1;
+
+  conn->read   = tls_socket_read;
+  conn->write  = tls_socket_write;
+  conn->close  = tls_starttls_close;
+
+  return 0;
+}
+
+static int protocol_priority[] = {GNUTLS_TLS1, GNUTLS_SSL3, 0};
+
+/* tls_negotiate: After TLS state has been initialised, attempt to negotiate
+ *   TLS over the wire, including certificate checks. */
+static int tls_negotiate (CONNECTION * conn)
+{
+  tlssockdata *data;
+  int err;
+
+  data = (tlssockdata *) safe_calloc (1, sizeof (tlssockdata));
+  conn->sockdata = data;
+  err = gnutls_certificate_allocate_credentials (&data->xcred);
+  if (err < 0)
+  {
+    FREE(&conn->sockdata);
+    mutt_error ("gnutls_certificate_allocate_credentials: %s", 
gnutls_strerror(err));
+    mutt_sleep (2);
+    return -1;
+  }
+
+  gnutls_certificate_set_x509_trust_file (data->xcred, SslCertFile,
+                                         GNUTLS_X509_FMT_PEM);
+  /* ignore errors, maybe file doesn't exist yet */
+
+  if (SslCACertFile)
+  {
+    gnutls_certificate_set_x509_trust_file (data->xcred, SslCACertFile,
+                                            GNUTLS_X509_FMT_PEM);
+  }
+
+/*
+  gnutls_set_x509_client_key (data->xcred, "", "");
+  gnutls_set_x509_cert_callback (data->xcred, cert_callback);
+*/
+
+  gnutls_init(&data->state, GNUTLS_CLIENT);
+
+  /* set socket */
+  gnutls_transport_set_ptr (data->state, (gnutls_transport_ptr)conn->fd);
+
+  /* disable TLS/SSL protocols as needed */
+  if (!option(OPTTLSV1) && !option(OPTSSLV3))
+  {
+    mutt_error (_("All available protocols for TLS/SSL connection disabled"));
+    goto fail;
+  }
+  else if (!option(OPTTLSV1))
+  {
+    protocol_priority[0] = GNUTLS_SSL3;
+    protocol_priority[1] = 0;
+  }
+  else if (!option(OPTSSLV3))
+  {
+    protocol_priority[0] = GNUTLS_TLS1;
+    protocol_priority[1] = 0;
+  }
+  /*
+  else
+    use the list set above
+  */
+
+  /* We use default priorities (see gnutls documentation),
+     except for protocol version */
+  gnutls_set_default_priority (data->state);
+  gnutls_protocol_set_priority (data->state, protocol_priority);
+
+  if (SslDHPrimeBits > 0)
+  {
+    gnutls_dh_set_prime_bits (data->state, SslDHPrimeBits);
+  }
+
+/*
+  gnutls_set_cred (data->state, GNUTLS_ANON, NULL);
+*/
+
+  gnutls_credentials_set (data->state, GNUTLS_CRD_CERTIFICATE, data->xcred);
+
+  err = gnutls_handshake(data->state);
+
+  while (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED)
+  {
+    err = gnutls_handshake(data->state);
+  }
+  if (err < 0) {
+    if (err == GNUTLS_E_FATAL_ALERT_RECEIVED)
+    {
+      mutt_error("gnutls_handshake: %s(%s)", gnutls_strerror(err),
+                gnutls_alert_get_name(gnutls_alert_get(data->state)));
+    }
+    else
+    {
+      mutt_error("gnutls_handshake: %s", gnutls_strerror(err));
+    }
+    mutt_sleep (2);
+    goto fail;
+  }
+
+  if (!tls_check_certificate(conn))
+    goto fail;
+
+  /* set Security Strength Factor (SSF) for SASL */
+  /* NB: gnutls_cipher_get_key_size() returns key length in bytes */
+  conn->ssf = gnutls_cipher_get_key_size (gnutls_cipher_get (data->state)) * 8;
+
+  mutt_message (_("SSL/TLS connection using %s (%s/%s/%s)"),
+               gnutls_protocol_get_name (gnutls_protocol_get_version 
(data->state)),
+               gnutls_kx_get_name (gnutls_kx_get (data->state)),
+               gnutls_cipher_get_name (gnutls_cipher_get (data->state)),
+               gnutls_mac_get_name (gnutls_mac_get (data->state)));
+  mutt_sleep (0);
+
+  return 0;
+
+ fail:
+  gnutls_certificate_free_credentials (data->xcred);
+  gnutls_deinit (data->state);
+  FREE(&conn->sockdata);
+  return -1;
+}
+
+static int tls_socket_close (CONNECTION* conn)
+{
+  tlssockdata *data = conn->sockdata;
+  if (data)
+  {
+    gnutls_bye (data->state, GNUTLS_SHUT_RDWR);
+
+    gnutls_certificate_free_credentials (data->xcred);
+    gnutls_deinit (data->state);
+    safe_free ((void **) &conn->sockdata);
+  }
+
+  return raw_socket_close (conn);
+}
+
+static int tls_starttls_close (CONNECTION* conn)
+{
+  int rc;
+
+  rc = tls_socket_close (conn);
+  conn->read = raw_socket_read;
+  conn->write = raw_socket_write;
+  conn->close = raw_socket_close;
+
+  return rc;
+}
+
+#define CERT_SEP "-----BEGIN"
+
+/* this bit is based on read_ca_file() in gnutls */
+static int tls_compare_certificates (const gnutls_datum *peercert)
+{
+  gnutls_datum cert;
+  unsigned char *ptr;
+  FILE *fd1;
+  int ret;
+  gnutls_datum b64_data;
+  unsigned char *b64_data_data;
+  struct stat filestat;
+
+  if (stat(SslCertFile, &filestat) == -1)
+    return 0;
+
+  b64_data.size = filestat.st_size+1;
+  b64_data_data = (unsigned char *) safe_calloc (1, b64_data.size);
+  b64_data_data[b64_data.size-1] = '\0';
+  b64_data.data = b64_data_data;
+
+  fd1 = fopen(SslCertFile, "r");
+  if (fd1 == NULL) {
+    return 0;
+  }
+
+  b64_data.size = fread(b64_data.data, 1, b64_data.size, fd1);
+  fclose(fd1);
+
+  do {
+    ret = gnutls_pem_base64_decode_alloc(NULL, &b64_data, &cert);
+    if (ret != 0)
+    {
+      FREE (&b64_data_data);
+      return 0;
+    }
+
+    ptr = (unsigned char *)strstr(b64_data.data, CERT_SEP) + 1;
+    ptr = (unsigned char *)strstr(ptr, CERT_SEP);
+
+    b64_data.size = b64_data.size - (ptr - b64_data.data);
+    b64_data.data = ptr;
+
+    if (cert.size == peercert->size)
+    {
+      if (memcmp (cert.data, peercert->data, cert.size) == 0)
+      {
+       /* match found */
+        gnutls_free(cert.data);
+       FREE (&b64_data_data);
+       return 1;
+      }
+    }
+
+    gnutls_free(cert.data);
+  } while (ptr != NULL);
+
+  /* no match found */
+  FREE (&b64_data_data);
+  return 0;
+}
+
+static void tls_fingerprint (gnutls_digest_algorithm algo,
+                             char* s, int l, const gnutls_datum* data)
+{
+  unsigned char md[36];
+  size_t n;
+  int j;
+
+  n = 36;
+
+  if (gnutls_fingerprint (algo, data, (char *)md, &n) < 0)
+  {
+    snprintf (s, l, _("[unable to calculate]"));
+  }
+  else
+  {
+    for (j = 0; j < (int) n; j++)
+    {
+      char ch[8];
+      snprintf (ch, 8, "%02X%s", md[j], (j % 2 ? " " : ""));
+      strncat (s, ch, l);
+    }
+    s[2*n+n/2-1] = '\0'; /* don't want trailing space */
+  }
+}
+
+static char *tls_make_date (time_t t, char *s, size_t len)
+{
+  struct tm *l = gmtime (&t);
+
+  if (l)
+    snprintf (s, len,  "%s, %d %s %d %02d:%02d:%02d UTC",
+             Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
+             l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec);
+  else
+    strfcpy (s, _("[invalid date]"), len);
+
+  return (s);
+}
+
+static int tls_check_stored_hostname (const gnutls_datum *cert,
+                                      const char *hostname)
+{
+  char buf[80];
+  FILE *fp;
+  char *linestr = NULL;
+  size_t linestrsize;
+  int linenum = 0;
+  regex_t preg;
+  regmatch_t pmatch[3];
+
+  /* try checking against names stored in stored certs file */
+  if ((fp = fopen (SslCertFile, "r")))
+  {
+    if (regcomp(&preg, "^#H ([a-zA-Z0-9_\\.-]+) ([0-9A-F]{4}( 
[0-9A-F]{4}){7})[ \t]*$", REG_ICASE|REG_EXTENDED) != 0)
+    {
+       regfree(&preg);
+       return 0;
+    }
+
+    buf[0] = '\0';
+    tls_fingerprint (GNUTLS_DIG_MD5, buf, sizeof (buf), cert);
+    while ((linestr = mutt_read_line(linestr, &linestrsize, fp, &linenum)) != 
NULL)
+    {
+      if(linestr[0] == '#' && linestr[1] == 'H')
+      {
+        if (regexec(&preg, linestr, 3, pmatch, 0) == 0)
+        {
+          linestr[pmatch[1].rm_eo] = '\0';
+          linestr[pmatch[2].rm_eo] = '\0';
+          if (strcmp(linestr + pmatch[1].rm_so, hostname) == 0 &&
+              strcmp(linestr + pmatch[2].rm_so, buf) == 0)
+          {
+            regfree(&preg);
+            safe_free((void**)&linestr);
+            fclose(fp);
+            return 1;
+          }
+        }
+      }
+    }
+
+    regfree(&preg);
+    fclose(fp);
+  }
+
+  /* not found a matching name */
+  return 0;
+}
+
+static int tls_check_certificate (CONNECTION* conn)
+{
+  tlssockdata *data = conn->sockdata;
+  gnutls_session state = data->state;
+  char helpstr[SHORT_STRING];
+  char buf[SHORT_STRING];
+  char fpbuf[SHORT_STRING];
+  int buflen;
+  char dn_common_name[SHORT_STRING];
+  char dn_email[SHORT_STRING];
+  char dn_organization[SHORT_STRING];
+  char dn_organizational_unit[SHORT_STRING];
+  char dn_locality[SHORT_STRING];
+  char dn_province[SHORT_STRING];
+  char dn_country[SHORT_STRING];
+  MUTTMENU *menu;
+  int done, row, i, ret;
+  FILE *fp;
+  gnutls_x509_dn dn;
+  time_t t;
+  const gnutls_datum *cert_list;
+  int cert_list_size = 0;
+  gnutls_certificate_status certstat;
+  char datestr[30];
+  gnutls_x509_crt cert;
+  gnutls_datum pemdata;
+  int certerr_expired = 0;
+  int certerr_notyetvalid = 0;
+  int certerr_hostname = 0;
+  int certerr_nottrusted = 0;
+  int certerr_revoked = 0;
+  int certerr_signernotca = 0;
+
+  if (gnutls_auth_get_type(state) != GNUTLS_CRD_CERTIFICATE)
+  {
+    mutt_error (_("Unable to get certificate from peer"));
+    mutt_sleep (2);
+    return 0;
+  }
+
+  certstat = gnutls_certificate_verify_peers(state);
+
+  if (certstat == GNUTLS_E_NO_CERTIFICATE_FOUND)
+  {
+    mutt_error (_("Unable to get certificate from peer"));
+    mutt_sleep (2);
+    return 0;
+  }
+  if (certstat < 0)
+  {
+    mutt_error (_("Certificate verification error (%s)"), 
gnutls_strerror(certstat));
+    mutt_sleep (2);
+    return 0;
+  }
+
+  /* We only support X.509 certificates (not OpenPGP) at the moment */
+  if (gnutls_certificate_type_get(state) != GNUTLS_CRT_X509)
+  {
+    mutt_error (_("Error certificate is not X.509"));
+    mutt_sleep (2);
+    return 0;
+  }
+
+  if (gnutls_x509_crt_init(&cert) < 0)
+  {
+    mutt_error (_("Error initialising gnutls certificate data"));
+    mutt_sleep (2);
+    return 0;
+  }
+
+  cert_list = gnutls_certificate_get_peers(state, &cert_list_size);
+  if (!cert_list)
+  {
+    mutt_error (_("Unable to get certificate from peer"));
+    mutt_sleep (2);
+    return 0;
+  }
+
+  /* FIXME: Currently only check first certificate in chain. */
+  if (gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0)
+  {
+    mutt_error (_("Error processing certificate data"));
+    mutt_sleep (2);
+    return 0;
+  }
+
+  if (gnutls_x509_crt_get_expiration_time(cert) < time(NULL))
+  {
+    certerr_expired = 1;
+  }
+
+  if (gnutls_x509_crt_get_activation_time(cert) > time(NULL))
+  {
+    certerr_notyetvalid = 1;
+  }
+
+  if (!gnutls_x509_crt_check_hostname(cert, conn->account.host) &&
+      !tls_check_stored_hostname (&cert_list[0], conn->account.host))
+  {
+    certerr_hostname = 1;
+  }
+
+  /* see whether certificate is in our cache (certificates file) */
+  if (tls_compare_certificates (&cert_list[0]))
+  {
+    if (certstat & GNUTLS_CERT_INVALID)
+    {
+      /* doesn't matter - have decided is valid because server
+         certificate is in our trusted cache */
+      certstat ^= GNUTLS_CERT_INVALID;
+    }
+
+    if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND)
+    {
+      /* doesn't matter that we haven't found the signer, since
+         certificate is in our trusted cache */
+      certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
+    }
+
+    if (certstat & GNUTLS_CERT_SIGNER_NOT_CA)
+    {
+      /* Hmm. Not really sure how to handle this, but let's say
+         that we don't care if the CA certificate hasn't got the
+         correct X.509 basic constraints if server certificate is
+         in our cache. */
+      certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
+    }
+
+  }
+
+  if (certstat & GNUTLS_CERT_REVOKED)
+  {
+    certerr_revoked = 1;
+    certstat ^= GNUTLS_CERT_REVOKED;
+  }
+
+  if (certstat & GNUTLS_CERT_INVALID)
+  {
+    certerr_nottrusted = 1;
+    certstat ^= GNUTLS_CERT_INVALID;
+  }
+
+  if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND)
+  {
+    /* NB: already cleared if cert in cache */
+    certerr_nottrusted = 1;
+    certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
+  }
+
+  if (certstat & GNUTLS_CERT_SIGNER_NOT_CA)
+  {
+    /* NB: already cleared if cert in cache */
+    certerr_signernotca = 1;
+    certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
+  }
+
+  /* OK if signed by (or is) a trusted certificate */
+  /* we've been zeroing the interesting bits in certstat - 
+     don't return OK if there are any unhandled bits we don't
+     understand */
+  if (!(certerr_expired || certerr_notyetvalid || 
+       certerr_hostname || certerr_nottrusted) && certstat == 0)
+  {
+    gnutls_x509_crt_deinit(cert);
+    return 1;
+  }
+
+
+  /* interactive check from user */
+  menu = mutt_new_menu ();
+  menu->max = 25;
+  menu->dialog = (char **) safe_calloc (1, menu->max * sizeof (char *));
+  for (i = 0; i < menu->max; i++)
+    menu->dialog[i] = (char *) safe_calloc (1, SHORT_STRING * sizeof (char));
+
+  row = 0;
+  strfcpy (menu->dialog[row], _("This certificate belongs to:"), SHORT_STRING);
+  row++;
+
+  buflen = sizeof(dn_common_name);
+  if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
+                                    dn_common_name, &buflen) != 0)
+    dn_common_name[0] = '\0';
+  buflen = sizeof(dn_email);
+  if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
+                                    dn_email, &buflen) != 0)
+    dn_email[0] = '\0';
+  buflen = sizeof(dn_organization);
+  if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 
0, 0,
+                                    dn_organization, &buflen) != 0)
+    dn_organization[0] = '\0';
+  buflen = sizeof(dn_organizational_unit);
+  if (gnutls_x509_crt_get_dn_by_oid(cert, 
GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
+                                    dn_organizational_unit, &buflen) != 0)
+    dn_organizational_unit[0] = '\0';
+  buflen = sizeof(dn_locality);
+  if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0,
+                                    dn_locality, &buflen) != 0)
+    dn_locality[0] = '\0';
+  buflen = sizeof(dn_province);
+  if (gnutls_x509_crt_get_dn_by_oid(cert, 
GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0,
+                                    dn_province, &buflen) != 0)
+    dn_province[0] = '\0';
+  buflen = sizeof(dn_country);
+  if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0,
+                                    dn_country, &buflen) != 0)
+    dn_country[0] = '\0';
+
+  snprintf (menu->dialog[row++], SHORT_STRING, "   %s  %s", dn_common_name, 
dn_email);
+  snprintf (menu->dialog[row++], SHORT_STRING, "   %s", dn_organization);
+  snprintf (menu->dialog[row++], SHORT_STRING, "   %s", 
dn_organizational_unit);
+  snprintf (menu->dialog[row++], SHORT_STRING, "   %s  %s  %s",
+            dn_locality, dn_province, dn_country);
+  row++;
+  
+  strfcpy (menu->dialog[row], _("This certificate was issued by:"), 
SHORT_STRING);
+  row++;
+
+  buflen = sizeof(dn_common_name);
+  if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 
0, 0,
+                                    dn_common_name, &buflen) != 0)
+    dn_common_name[0] = '\0';
+  buflen = sizeof(dn_email);
+  if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
+                                    dn_email, &buflen) != 0)
+    dn_email[0] = '\0';
+  buflen = sizeof(dn_organization);
+  if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, 
GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0,
+                                    dn_organization, &buflen) != 0)
+    dn_organization[0] = '\0';
+  buflen = sizeof(dn_organizational_unit);
+  if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, 
GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
+                                    dn_organizational_unit, &buflen) != 0)
+    dn_organizational_unit[0] = '\0';
+  buflen = sizeof(dn_locality);
+  if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, 
GNUTLS_OID_X520_LOCALITY_NAME, 0, 0,
+                                    dn_locality, &buflen) != 0)
+    dn_locality[0] = '\0';
+  buflen = sizeof(dn_province);
+  if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, 
GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0,
+                                    dn_province, &buflen) != 0)
+    dn_province[0] = '\0';
+  buflen = sizeof(dn_country);
+  if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_COUNTRY_NAME, 
0, 0,
+                                    dn_country, &buflen) != 0)
+    dn_country[0] = '\0';
+
+  snprintf (menu->dialog[row++], SHORT_STRING, "   %s  %s", dn_common_name, 
dn_email);
+  snprintf (menu->dialog[row++], SHORT_STRING, "   %s", dn_organization);
+  snprintf (menu->dialog[row++], SHORT_STRING, "   %s", 
dn_organizational_unit);
+  snprintf (menu->dialog[row++], SHORT_STRING, "   %s  %s  %s",
+            dn_locality, dn_province, dn_country);
+  row++;
+
+  snprintf (menu->dialog[row++], SHORT_STRING, _("This certificate is valid"));
+
+  t = gnutls_x509_crt_get_activation_time(cert);
+  snprintf (menu->dialog[row++], SHORT_STRING, _("   from %s"), 
+           tls_make_date(t, datestr, 30));
+
+  t = gnutls_x509_crt_get_expiration_time(cert);
+  snprintf (menu->dialog[row++], SHORT_STRING, _("     to %s"), 
+           tls_make_date(t, datestr, 30));
+
+  fpbuf[0] = '\0';
+  tls_fingerprint (GNUTLS_DIG_SHA, fpbuf, sizeof (fpbuf), &cert_list[0]);
+  snprintf (menu->dialog[row++], SHORT_STRING, _("SHA1 Fingerprint: %s"), 
fpbuf);
+  fpbuf[0] = '\0';
+  tls_fingerprint (GNUTLS_DIG_MD5, fpbuf, sizeof (fpbuf), &cert_list[0]);
+  snprintf (menu->dialog[row++], SHORT_STRING, _("MD5 Fingerprint: %s"), 
fpbuf);
+
+  if (certerr_notyetvalid)
+  {
+    row++;
+    strfcpy (menu->dialog[row], _("WARNING: Server certificate is not yet 
valid"), SHORT_STRING);
+  }
+  if (certerr_expired)
+  {
+    row++;
+    strfcpy (menu->dialog[row], _("WARNING: Server certificate has expired"), 
SHORT_STRING);
+  }
+  if (certerr_revoked)
+  {
+    row++;
+    strfcpy (menu->dialog[row], _("WARNING: Server certificate has been 
revoked"), SHORT_STRING);
+  }
+  if (certerr_hostname)
+  {
+    row++;
+    strfcpy (menu->dialog[row], _("WARNING: Server hostname does not match 
certificate"), SHORT_STRING);
+  }
+  if (certerr_signernotca)
+  {
+    row++;
+    strfcpy (menu->dialog[row], _("WARNING: Signer of server certificate is 
not a CA"), SHORT_STRING);
+  }
+
+  menu->title = _("TLS/SSL Certificate check");
+  /* certificates with bad dates, or that are revoked, must be
+     accepted manually each and every time */
+  if (SslCertFile && !certerr_expired && !certerr_notyetvalid && 
!certerr_revoked)
+  {
+    menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
+    menu->keys = _("roa");
+  }
+  else
+  {
+    menu->prompt = _("(r)eject, accept (o)nce");
+    menu->keys = _("ro");
+  }
+  
+  helpstr[0] = '\0';
+  mutt_make_help (buf, sizeof (buf), _("Exit  "), MENU_GENERIC, OP_EXIT);
+  strncat (helpstr, buf, sizeof (helpstr));
+  mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP);
+  strncat (helpstr, buf, sizeof (helpstr));
+  menu->help = helpstr;
+
+  done = 0;
+  while (!done)
+  {
+    switch (mutt_menuLoop (menu))
+    {
+      case -1:                 /* abort */
+      case OP_MAX + 1:         /* reject */
+      case OP_EXIT:
+        done = 1;
+        break;
+      case OP_MAX + 3:         /* accept always */
+        done = 0;
+        if ((fp = fopen (SslCertFile, "a")))
+       {
+         /* save hostname if necessary */
+         if (certerr_hostname)
+         {
+           fprintf(fp, "#H %s %s\n", conn->account.host, fpbuf);
+           done = 1;
+         }
+         if (certerr_nottrusted)
+         {
+            done = 0;
+           ret = gnutls_pem_base64_encode_alloc ("CERTIFICATE", &cert_list[0],
+                                                  &pemdata);
+           if (ret == 0)
+           {
+             if (fwrite(pemdata.data, pemdata.size, 1, fp) == 1)
+             {
+               done = 1;
+             }
+              gnutls_free(pemdata.data);
+           }
+         }
+         fclose (fp);
+       }
+       if (!done)
+        {
+         mutt_error (_("Warning: Couldn't save certificate"));
+         mutt_sleep (2);
+       }
+       else
+        {
+         mutt_message (_("Certificate saved"));
+         mutt_sleep (0);
+       }
+        /* fall through */
+      case OP_MAX + 2:         /* accept once */
+        done = 2;
+        break;
+    }
+  }
+  mutt_menuDestroy (&menu);
+  gnutls_x509_crt_deinit(cert);
+  return (done == 2);
+}
# vi: ft=diff
This patch by Alexander Neumann <alexander@xxxxxxxxxx> adds POP TLS
support to the gnutls patch.

It was received at Debian's BTS, Bug#260638.

* Patch last synced with upstream:
  - Date: 2004-08-05
  - File: 
http://bugs.debian.org/cgi-bin/bugreport.cgi/mutt-gnutls-pop.patch?bug=260638&msg=3&att=1

* Changes made:
  - 2004-08-05: removed the correct-typo-in-comment part.
  
== END PATCH
--- mutt/pop_lib.c.orig 2004-07-21 15:18:49.000000000 +0200
+++ mutt/pop_lib.c      2004-07-21 15:58:58.000000000 +0200
@@ -270,7 +270,7 @@
     return -2;
   }
 
-#if defined(USE_SSL) && !defined(USE_NSS)
+#if (defined(USE_SSL) || defined(USE_GNUTLS)) && !defined(USE_NSS)
   /* Attempt STLS if available and desired. */
   if (pop_data->cmd_stls && !pop_data->conn->ssf)
   {
@@ -295,7 +295,11 @@
        mutt_error ("%s", pop_data->err_msg);
        mutt_sleep (2);
       }
+#ifdef USE_SSL
       else if (mutt_ssl_starttls (pop_data->conn))
+#elif USE_GNUTLS
+      else if (mutt_gnutls_starttls (pop_data->conn))
+#endif
       {
        mutt_error (_("Could not negotiate TLS connection"));
        mutt_sleep (2);
# vi: ft=diff
This is the gnutlsbuild patch by Andrew McDonald <andrew@xxxxxxxxxxxxxxx>.

The home page for this patch is:

  http://www.mcdonald.org.uk/andrew/mutt-gnutls/

* Patch last synced with upstream:
  - Date: unknown.
  - File: 
http://www.mcdonald.org.uk/andrew/mutt-gnutls/012_patch-1.4.admcd.gnutlsbuild.53

* Changes made:
  - leave only the init.h diff.

== END PATCH
diff -ruN mutt-1.5.5.1.orig/init.h mutt-1.5.5.1/init.h
--- mutt-1.5.5.1.orig/init.h    2003-12-22 02:01:14.000000000 +0100
+++ mutt-1.5.5.1/init.h 2003-12-22 01:49:37.000000000 +0100
@@ -1746,7 +1746,7 @@
   ** use STARTTLS regardless of the server's capabilities.
   */
 # endif  
-  { "certificate_file",        DT_PATH, R_NONE, UL &SslCertFile, 0 },
+  { "certificate_file",        DT_PATH, R_NONE, UL &SslCertFile, UL 
"~/.mutt_certificates" },
   /*
   ** .pp
   ** This variable specifies the file where the certificates you trust
# vi: ft=diff
This is the compat patch by Takashi TAKIZAWA <taki@xxxxxxxxxxxxxxxxx>.

The home page for this patch is:

  http://www.emaillab.org/mutt/download15.html.en

* Patch last synced with upstream:
  - Date: 2004-07-22
  - File: http://www.emaillab.org/mutt/1.5/patch-1.5.6.tt.assumed_charset.1.gz
  - File: 
http://www.emaillab.org/mutt/1.5/patch-1.5.6.tt.adjust_edited_file.1.gz
  - File: 
http://www.emaillab.org/mutt/1.5/patch-1.5.6.tt.adjust_edited_file.1.gz
  - File: http://www.emaillab.org/mutt/1.5/patch-1.5.6.tt.adjust_line.3.gz

* Changes made:
  - 2004-07-22: merge all PATCHES entries into a single one.

== END PATCH
diff -uNr mutt-1.5.6.orig/PATCHES mutt-1.5.6/PATCHES
--- mutt-1.5.6.orig/PATCHES     Mon Feb  2 02:42:47 2004
+++ mutt-1.5.6/PATCHES  Sun Feb 15 18:12:21 2004
@@ -0,0 +1 @@
+patch-1.5.6.tt.compat.1
diff -uNr mutt-1.5.6.orig/curs_lib.c mutt-1.5.6/curs_lib.c
--- mutt-1.5.6.orig/curs_lib.c  Wed Nov  5 18:41:31 2003
+++ mutt-1.5.6/curs_lib.c       Sun Feb 15 18:12:11 2004
@@ -43,6 +43,19 @@
 static size_t UngetBufLen = 0;
 static event_t *KeyEvent;
 
+static void adjust_edited_file (const char *data)
+{
+  FILE *fp;
+  int c;
+
+  if ((fp = safe_fopen (data, "a+")) == NULL)
+    return;
+  fseek (fp,-1,SEEK_END);
+  if ((c = fgetc(fp)) != '\n')
+    fputc ('\n', fp);
+  safe_fclose (&fp);
+}
+
 void mutt_refresh (void)
 {
   /* don't refresh when we are waiting for a child. */
@@ -147,6 +160,7 @@
   mutt_expand_file_fmt (cmd, sizeof (cmd), editor, data);
   if (mutt_system (cmd) == -1)
     mutt_error (_("Error running \"%s\"!"), cmd);
+  adjust_edited_file (data);
   keypad (stdscr, TRUE);
   clearok (stdscr, TRUE);
 }
diff -ur mutt-1.5.6.orig/curs_lib.c mutt-1.5.6/curs_lib.c
--- mutt-1.5.6.orig/curs_lib.c  Wed Nov  5 18:41:31 2003
+++ mutt-1.5.6/curs_lib.c       Tue May 25 23:37:27 2004
@@ -575,7 +575,7 @@
       wc = replacement_char ();
     }
     if (arboreal && wc < M_TREE_MAX)
-      w = 1; /* hack */
+      w = mutt_mbswidth ((char *)s,1);
     else
     {
       if (!IsWPrint (wc))
diff -ur mutt-1.5.6.orig/muttlib.c mutt-1.5.6/muttlib.c
--- mutt-1.5.6.orig/muttlib.c   Mon Feb  2 02:15:17 2004
+++ mutt-1.5.6/muttlib.c        Tue May 25 23:47:27 2004
@@ -23,6 +23,7 @@
 #include "mailbox.h"
 #include "mx.h"
 #include "url.h"
+#include "mbyte.h"
 
 #include "reldate.h"
 
@@ -913,6 +914,43 @@
 }
 
 
+/* return col of multibyte character string */
+size_t mutt_mbswidth (char *s, size_t n)
+{
+  wchar_t wc;
+  size_t k,l,m;
+  mbstate_t mbstate;
+  unsigned int i;
+
+  l = 0;
+  if (!s)
+    return 0;
+  memset (&mbstate, 0, sizeof (mbstate));
+  while (*s && n)
+  {
+    i = (unsigned int)*s;
+    if (i < M_TREE_MAX)
+    {
+      if (Charset_is_utf8)
+        l += 1; /* FIXME: width of WACS character is not always 1 */
+      else
+        l++;
+      s++, n--;
+    }
+    else
+    {
+      k = mbrtowc (&wc, s, n, &mbstate);
+      if (k == (size_t)(-1) || k == (size_t)(-2))
+        break;
+      m = wcwidth (wc);
+      l += (m > 0) ? m : 0;
+      s += k, n -= k;
+    }
+  }
+  return l;
+}
+
+
 void mutt_FormatString (char *dest,            /* output buffer */
                        size_t destlen,         /* output buffer len */
                        const char *src,        /* template string */
@@ -922,11 +960,12 @@
 {
   char prefix[SHORT_STRING], buf[LONG_STRING], *cp, *wptr = dest, ch;
   char ifstring[SHORT_STRING], elsestring[SHORT_STRING];
-  size_t wlen, count, len;
+  size_t wlen, count, len, col, wid;
 
   prefix[0] = '\0';
   destlen--; /* save room for the terminal \0 */
   wlen = (flags & M_FORMAT_ARROWCURSOR && option (OPTARROWCURSOR)) ? 3 : 0;
+  col = wlen;
     
   while (*src && wlen < destlen)
   {
@@ -936,6 +975,7 @@
       {
        *wptr++ = '%';
        wlen++;
+       col++;
        src++;
        continue;
       }
@@ -1008,23 +1048,26 @@
        /* calculate space left on line.  if we've already written more data
           than will fit on the line, ignore the rest of the line */
        count = (COLS < destlen ? COLS : destlen);
-       if (count > wlen)
+       if (count > col)
        {
-         count -= wlen; /* how many chars left on this line */
+         count -= col; /* how many columns left on this line */
          mutt_FormatString (buf, sizeof (buf), src, callback, data, flags);
          len = mutt_strlen (buf);
-         if (count > len)
+         wid = mutt_mbswidth (buf, len);
+         if (count > wid)
          {
-           count -= len; /* how many chars to pad */
+           count -= wid; /* how many chars to pad */
            memset (wptr, ch, count);
            wptr += count;
            wlen += count;
+           col += count;
          }
          if (len + wlen > destlen)
            len = destlen - wlen;
          memcpy (wptr, buf, len);
          wptr += len;
          wlen += len;
+         col += mutt_mbswidth (buf, len);
        }
        break; /* skip rest of input */
       }
@@ -1076,6 +1119,7 @@
        memcpy (wptr, buf, len);
        wptr += len;
        wlen += len;
+       col += mutt_mbswidth (buf, len);
       }
     }
     else if (*src == '\\')
@@ -1106,11 +1150,13 @@
       src++;
       wptr++;
       wlen++;
+      col++;
     }
     else
     {
       *wptr++ = *src++;
       wlen++;
+      col++;
     }
   }
   *wptr = 0;
diff -ur mutt-1.5.6.orig/pager.c mutt-1.5.6/pager.c
--- mutt-1.5.6.orig/pager.c     Mon Feb  2 02:10:57 2004
+++ mutt-1.5.6/pager.c  Tue May 25 23:37:27 2004
@@ -1706,15 +1706,17 @@
       CLEARLINE (statusoffset);
       if (IsHeader (extra))
       {
-       _mutt_make_string (buffer,
-                          COLS-9 < sizeof (buffer) ? COLS-9 : sizeof (buffer),
-                          NONULL (PagerFmt), Context, extra->hdr, 
M_FORMAT_MAKEPRINT);
+       size_t l1 = (COLS - 9) * MB_LEN_MAX;
+       size_t l2 = sizeof (buffer);
+       _mutt_make_string (buffer, l1 < l2 ? l1 : l2, NONULL (PagerFmt),
+                          Context, extra->hdr, M_FORMAT_MAKEPRINT);
       }
       else if (IsMsgAttach (extra))
       {
-       _mutt_make_string (buffer,
-                          COLS - 9 < sizeof (buffer) ? COLS - 9: sizeof 
(buffer),
-                          NONULL (PagerFmt), Context, extra->bdy->hdr, 
M_FORMAT_MAKEPRINT);
+       size_t l1 = (COLS - 9) * MB_LEN_MAX;
+       size_t l2 = sizeof (buffer);
+       _mutt_make_string (buffer, l1 < l2 ? l1 : l2, NONULL (PagerFmt),
+                          Context, extra->bdy->hdr, M_FORMAT_MAKEPRINT);
       }
       mutt_paddstr (COLS-10, IsHeader (extra) || IsMsgAttach (extra) ?
                    buffer : banner);
diff -ur mutt-1.5.6.orig/protos.h mutt-1.5.6/protos.h
--- mutt-1.5.6.orig/protos.h    Mon Feb  2 02:15:17 2004
+++ mutt-1.5.6/protos.h Tue May 25 23:37:27 2004
@@ -350,6 +350,7 @@
 
 /* utf8.c */
 int mutt_wctoutf8 (char *s, unsigned int c);
+size_t mutt_mbswidth (char *s, size_t n);
 
 #ifdef LOCALES_HACK
 #define IsPrint(c) (isprint((unsigned char)(c)) || \
diff -uNr mutt-1.5.6.orig/charset.c mutt-1.5.6/charset.c
--- mutt-1.5.6.orig/charset.c   Tue Jan 21 21:25:21 2003
+++ mutt-1.5.6/charset.c        Sun Feb 15 15:13:26 2004
@@ -581,3 +581,86 @@
     iconv_close (fc->cd);
   FREE (_fc);
 }
+
+char *mutt_get_first_charset (const char *charset)
+{
+  static char fcharset[SHORT_STRING];
+  const char *c, *c1;
+
+  c = charset;
+  if (!mutt_strlen(c))
+    return "us-ascii";
+  if (!(c1 = strchr (c, ':')))
+    return charset;
+  strfcpy (fcharset, c, c1 - c + 1);
+  return fcharset;
+}
+
+static size_t convert_string (ICONV_CONST char *f, size_t flen,
+                             const char *from, const char *to,
+                             char **t, size_t *tlen)
+{
+  iconv_t cd;
+  char *buf, *ob;
+  size_t obl, n;
+  int e;
+
+  cd = mutt_iconv_open (to, from, 0);
+  if (cd == (iconv_t)(-1))
+    return (size_t)(-1);
+  obl = 4 * flen + 1;
+  ob = buf = safe_malloc (obl);
+  n = iconv (cd, &f, &flen, &ob, &obl);
+  if (n == (size_t)(-1) || iconv (cd, 0, 0, &ob, &obl) == (size_t)(-1))
+  {
+    e = errno;
+    FREE (&buf);
+    iconv_close (cd);
+    errno = e;
+    return (size_t)(-1);
+  }
+  *ob = '\0';
+
+  *tlen = ob - buf;
+
+  safe_realloc ((void **) &buf, ob - buf + 1);
+  *t = buf;
+  iconv_close (cd);
+
+  return n;
+}
+
+int mutt_convert_nonmime_string (char **ps)
+{
+  const char *c, *c1;
+
+  for (c = AssumedCharset; c; c = c1 ? c1 + 1 : 0)
+  {
+    char *u = *ps;
+    char *s;
+    char *fromcode;
+    size_t m, n;
+    size_t ulen = mutt_strlen (*ps);
+    size_t slen;
+
+    if (!u || !*u)
+      return 0;
+
+    c1 = strchr (c, ':');
+    n = c1 ? c1 - c : mutt_strlen (c);
+    if (!n)
+      continue;
+    fromcode = safe_malloc (n + 1);
+    strfcpy (fromcode, c, n + 1);
+    m = convert_string (u, ulen, fromcode, Charset, &s, &slen);
+    FREE (&fromcode);
+    if (m != (size_t)(-1))
+    {
+      FREE (ps);
+      *ps = s;
+      return 0;
+    }
+  }
+  return -1;
+}
+
diff -uNr mutt-1.5.6.orig/charset.h mutt-1.5.6/charset.h
--- mutt-1.5.6.orig/charset.h   Tue Mar  4 16:49:43 2003
+++ mutt-1.5.6/charset.h        Sun Feb 15 15:06:19 2004
@@ -35,6 +35,8 @@
 #endif
 
 int mutt_convert_string (char **, const char *, const char *, int);
+char *mutt_get_first_charset (const char *);
+int mutt_convert_nonmime_string (char **);
 
 iconv_t mutt_iconv_open (const char *, const char *, int);
 size_t mutt_iconv (iconv_t, ICONV_CONST char **, size_t *, char **, size_t *, 
ICONV_CONST char **, const char *);
diff -uNr mutt-1.5.6.orig/globals.h mutt-1.5.6/globals.h
--- mutt-1.5.6.orig/globals.h   Mon Feb  2 02:15:17 2004
+++ mutt-1.5.6/globals.h        Sun Feb 15 15:06:19 2004
@@ -32,6 +32,7 @@
 
 WHERE char *AliasFile;
 WHERE char *AliasFmt;
+WHERE char *AssumedCharset;
 WHERE char *AttachSep;
 WHERE char *Attribution;
 WHERE char *AttachFormat;
@@ -45,6 +46,7 @@
 WHERE char *DsnReturn;
 WHERE char *Editor;
 WHERE char *EscChar;
+WHERE char *FileCharset;
 WHERE char *FolderFormat;
 WHERE char *ForwFmt;
 WHERE char *Fqdn;
diff -uNr mutt-1.5.6.orig/handler.c mutt-1.5.6/handler.c
--- mutt-1.5.6.orig/handler.c   Wed Nov  5 18:41:31 2003
+++ mutt-1.5.6/handler.c        Sun Feb 15 15:06:19 2004
@@ -1718,11 +1718,21 @@
   int istext = mutt_is_text_part (b);
   iconv_t cd = (iconv_t)(-1);
 
-  if (istext && s->flags & M_CHARCONV)
+  if (istext)
   {
-    char *charset = mutt_get_parameter ("charset", b->parameter);
-    if (charset && Charset)
-      cd = mutt_iconv_open (Charset, charset, M_ICONV_HOOK_FROM);
+    if(s->flags & M_CHARCONV)
+    {
+      char *charset = mutt_get_parameter ("charset", b->parameter);
+      if (!option (OPTSTRICTMIME) && !charset)
+        charset = mutt_get_first_charset (AssumedCharset);
+      if (charset && Charset)
+        cd = mutt_iconv_open (Charset, charset, M_ICONV_HOOK_FROM);
+    }
+    else
+    {
+      if (b->file_charset)
+        cd = mutt_iconv_open (Charset, b->file_charset, M_ICONV_HOOK_FROM);
+    }
   }
 
   fseek (s->fpin, b->offset, 0);
diff -uNr mutt-1.5.6.orig/init.h mutt-1.5.6/init.h
--- mutt-1.5.6.orig/init.h      Mon Feb  2 02:15:17 2004
+++ mutt-1.5.6/init.h   Sun Feb 15 15:07:40 2004
@@ -184,6 +184,23 @@
   ** If set, Mutt will prompt you for carbon-copy (Cc) recipients before
   ** editing the body of an outgoing message.
   */  
+  { "assumed_charset", DT_STR, R_NONE, UL &AssumedCharset, UL "us-ascii"},
+  /*
+  ** .pp
+  ** This variable is a colon-separated list of character encoding
+  ** schemes for messages without character encoding indication.
+  ** Header field values and message body content without character encoding
+  ** indication would be assumed that they are written in one of this list.
+  ** By default, all the header fields and message body without any charset
+  ** indication are assumed to be in "us-ascii".
+  ** .pp
+  ** For example, Japanese users might prefer this:
+  ** .pp
+  **   set assumed_charset="iso-2022-jp:euc-jp:shift_jis:utf-8"
+  ** .pp
+  ** However, only the first content is valid for the message body.
+  ** This variable is valid only if $$strict_mime is unset.
+  */
   { "attach_format",   DT_STR,  R_NONE, UL &AttachFormat, UL "%u%D%I %t%4n 
%T%.40d%> [%.7m/%.10M, %.6e%?C?, %C?, %s] " },
   /*
   ** .pp
@@ -532,6 +549,20 @@
   ** signed.
   ** (PGP only)
   */
+  { "file_charset",    DT_STR,  R_NONE, UL &FileCharset, UL 0 },
+  /*
+  ** .pp
+  ** This variable is a colon-separated list of character encoding
+  ** schemes for text file attatchments.
+  ** If unset, $$charset value will be used instead.
+  ** For example, the following configuration would work for Japanese
+  ** text handling:
+  ** .pp
+  **   set file_charset="iso-2022-jp:euc-jp:shift_jis:utf-8"
+  ** .pp
+  ** Note: "iso-2022-*" must be put at the head of the value as shown above
+  ** if included.
+  */
   { "folder",          DT_PATH, R_NONE, UL &Maildir, UL "~/Mail" },
   /*
   ** .pp
@@ -2476,6 +2507,19 @@
   ** Setting this variable causes the ``status bar'' to be displayed on
   ** the first line of the screen rather than near the bottom.
   */
+  { "strict_mime",    DT_BOOL, R_NONE, OPTSTRICTMIME, 1 },
+  /*
+  ** .pp
+  ** When unset, non MIME-compliant messages that doesn't have any
+  ** charset indication in ``Content-Type'' field can be displayed
+  ** (non MIME-compliant messages are often generated by old mailers
+  ** or buggy mailers like MS Outlook Express).
+  ** See also $$assumed_charset.
+  ** .pp
+  ** This option also replaces linear-white-space between encoded-word
+  ** and *text to a single space to prevent the display of MIME-encoded
+  ** ``Subject'' field from being devided into multiple lines.
+  */
   { "strict_threads",  DT_BOOL, R_RESORT|R_RESORT_INIT|R_INDEX, 
OPTSTRICTTHREADS, 0 },
   /*
   ** .pp
diff -uNr mutt-1.5.6.orig/mutt.h mutt-1.5.6/mutt.h
--- mutt-1.5.6.orig/mutt.h      Mon Feb  2 02:15:17 2004
+++ mutt-1.5.6/mutt.h   Sun Feb 15 15:06:19 2004
@@ -406,6 +406,7 @@
   OPTSIGONTOP,
   OPTSORTRE,
   OPTSTATUSONTOP,
+  OPTSTRICTMIME,
   OPTSTRICTTHREADS,
   OPTSUSPEND,
   OPTTEXTFLOWED,
@@ -599,6 +600,7 @@
                                 * If NULL, filename is used 
                                 * instead.
                                 */
+  char *file_charset;           /* charset of attached file */
   CONTENT *content;             /* structure used to store detailed info about
                                 * the content of the attachment.  this is used
                                 * to determine what content-transfer-encoding
diff -uNr mutt-1.5.6.orig/parse.c mutt-1.5.6/parse.c
--- mutt-1.5.6.orig/parse.c     Wed Nov  5 18:41:33 2003
+++ mutt-1.5.6/parse.c  Sun Feb 15 15:06:19 2004
@@ -208,9 +208,23 @@
 
       if (*s == '"')
       {
+        int state_ascii = 1;
        s++;
-       for (i=0; *s && *s != '"' && i < sizeof (buffer) - 1; i++, s++)
+       for (i=0; *s && i < sizeof (buffer) - 1; i++, s++)
        {
+         if (!option (OPTSTRICTMIME)) {
+            /* As iso-2022-* has a characer of '"' with non-ascii state,
+            * ignore it. */
+            if (*s == 0x1b && i < sizeof (buffer) - 2)
+            {
+              if (s[1] == '(' && (s[2] == 'B' || s[2] == 'J'))
+                state_ascii = 1;
+              else
+                state_ascii = 0;
+            }
+          }
+          if (state_ascii && *s == '"')
+            break;
          if (*s == '\\')
          {
            /* Quote the next character */
@@ -379,7 +393,9 @@
   if (ct->type == TYPETEXT)
   {
     if (!(pc = mutt_get_parameter ("charset", ct->parameter)))
-      mutt_set_parameter ("charset", "us-ascii", &ct->parameter);
+      mutt_set_parameter ("charset", option (OPTSTRICTMIME) ? "us-ascii" :
+                         (const char *) mutt_get_first_charset 
(AssumedCharset),
+                         &ct->parameter);
   }
 
 }
diff -uNr mutt-1.5.6.orig/rfc2047.c mutt-1.5.6/rfc2047.c
--- mutt-1.5.6.orig/rfc2047.c   Wed Nov  5 18:41:33 2003
+++ mutt-1.5.6/rfc2047.c        Sun Feb 15 15:13:58 2004
@@ -706,13 +706,54 @@
   return 0;
 }
 
+/* return length of linear white space */
+static size_t lwslen (const char *s, size_t n)
+{
+  const char *p = s;
+  size_t len = n;
+
+  if (n <= 0)
+    return 0;
+
+  for (; p < s + n; p++)
+    if (!strchr (" \t\r\n", *p))
+    {
+      len = (size_t)(p - s);
+      break;
+    }
+  if (strchr ("\r\n", *(p-1))) /* LWS doesn't end with CRLF */
+    len = (size_t)0;
+  return len;
+}
+
+/* return length of linear white space : reverse */
+static size_t lwsrlen (const char *s, size_t n)
+{
+  const char *p = s + n - 1;
+  size_t len = n;
+
+  if (n <= 0)
+    return 0;
+
+  if (strchr ("\r\n", *p)) /* LWS doesn't end with CRLF */
+    return (size_t)0;
+
+  for (; p >= s; p--)
+    if (!strchr (" \t\r\n", *p))
+    {
+      len = (size_t)(s + n - 1 - p);
+      break;
+    }
+  return len;
+}
+
 /* try to decode anything that looks like a valid RFC2047 encoded
  * header field, ignoring RFC822 parsing rules
  */
 void rfc2047_decode (char **pd)
 {
   const char *p, *q;
-  size_t n;
+  size_t m, n;
   int found_encoded = 0;
   char *d0, *d;
   const char *s = *pd;
@@ -729,6 +770,37 @@
     if (!(p = find_encoded_word (s, &q)))
     {
       /* no encoded words */
+      if (!option (OPTSTRICTMIME))
+      {
+        n = mutt_strlen (s);
+        if (found_encoded && (m = lwslen (s, n)) != 0)
+        {
+          if (m != n)
+            *d = ' ', d++, dlen--;
+          n -= m, s += m;
+        }
+        if (ascii_strcasecmp (AssumedCharset, "us-ascii"))
+        {
+          char *t;
+          size_t tlen;
+
+          t = safe_malloc (n + 1);
+          strfcpy (t, s, n + 1);
+          if (mutt_convert_nonmime_string (&t) == 0)
+          {
+            tlen = mutt_strlen (t);
+            strncpy (d, t, tlen);
+            d += tlen;
+          }
+          else
+          {
+            strncpy (d, s, n);
+            d += n;
+          }
+          FREE (&t);
+          break;
+        }
+      }
       strncpy (d, s, dlen);
       d += dlen;
       break;
@@ -737,15 +809,37 @@
     if (p != s)
     {
       n = (size_t) (p - s);
-      /* ignore spaces between encoded words */
-      if (!found_encoded || strspn (s, " \t\r\n") != n)
+      /* ignore spaces between encoded words
+       * and linear white spaces between encoded word and *text */
+      if (!option (OPTSTRICTMIME))
       {
-       if (n > dlen)
-         n = dlen;
-       memcpy (d, s, n);
-       d += n;
-       dlen -= n;
+        if (found_encoded && (m = lwslen (s, n)) != 0)
+        {
+          if (m != n)
+            *d = ' ', d++, dlen--;
+          n -= m, s += m;
+        }
+
+        if ((m = n - lwsrlen (s, n)) != 0)
+        {
+          if (m > dlen)
+            m = dlen;
+          memcpy (d, s, m);
+          d += m;
+          dlen -= m;
+          if (m != n)
+            *d = ' ', d++, dlen--;
+        }
       }
+      else if (!found_encoded || strspn (s, " \t\r\n") != n)
+      {
+        if (n > dlen)
+          n = dlen;
+        memcpy (d, s, n);
+        d += n;
+        dlen -= n;
+      }
+
     }
 
     rfc2047_decode_word (d, p, dlen);
@@ -766,7 +860,7 @@
 {
   while (a)
   {
-    if (a->personal && strstr (a->personal, "=?") != NULL)
+    if (a->personal)
       rfc2047_decode (&a->personal);
 #ifdef EXACT_ADDRESS
     if (a->val && strstr (a->val, "=?") != NULL)
diff -uNr mutt-1.5.6.orig/rfc2231.c mutt-1.5.6/rfc2231.c
--- mutt-1.5.6.orig/rfc2231.c   Wed Nov  5 18:41:33 2003
+++ mutt-1.5.6/rfc2231.c        Sun Feb 15 15:06:19 2004
@@ -113,6 +113,11 @@
 
       if (option (OPTRFC2047PARAMS) && p->value && strstr (p->value, "=?"))
        rfc2047_decode (&p->value);
+      else if (!option (OPTSTRICTMIME))
+      {
+        if (ascii_strcasecmp (AssumedCharset, "us-ascii"))
+          mutt_convert_nonmime_string (&p->value);
+      }
 
       *last = p;
       last = &p->next;
diff -uNr mutt-1.5.6.orig/sendlib.c mutt-1.5.6/sendlib.c
--- mutt-1.5.6.orig/sendlib.c   Wed Nov  5 18:41:33 2003
+++ mutt-1.5.6/sendlib.c        Sun Feb 15 15:11:33 2004
@@ -496,7 +496,7 @@
   }
 
   if (a->type == TYPETEXT && (!a->noconv))
-    fc = fgetconv_open (fpin, Charset, 
+    fc = fgetconv_open (fpin, a->file_charset, 
                        mutt_get_body_charset (send_charset, sizeof 
(send_charset), a),
                        0);
   else
@@ -896,6 +896,7 @@
   CONTENT *info;
   CONTENT_STATE state;
   FILE *fp = NULL;
+  char *fromcode;
   char *tocode;
   char buffer[100];
   char chsbuf[STRING];
@@ -930,15 +931,18 @@
   if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
   {
     char *chs = mutt_get_parameter ("charset", b->parameter);
+    char *fchs = b->use_disp ? ((FileCharset && *FileCharset) ?
+                                FileCharset : Charset) : Charset;
     if (Charset && (chs || SendCharset) &&
-       convert_file_from_to (fp, Charset, chs ? chs : SendCharset,
-                             0, &tocode, info) != (size_t)(-1))
+        convert_file_from_to (fp, fchs, chs ? chs : SendCharset,
+                              &fromcode, &tocode, info) != (size_t)(-1))
     {
       if (!chs)
       {
        mutt_canonical_charset (chsbuf, sizeof (chsbuf), tocode);
        mutt_set_parameter ("charset", chsbuf, &b->parameter);
       }
+      b->file_charset = fromcode;
       FREE (&tocode);
       safe_fclose (&fp);
       return info;
@@ -1318,6 +1322,7 @@
   body->unlink = 1;
   body->use_disp = 0;
   body->disposition = DISPINLINE;
+  body->noconv = 1;
 
   mutt_parse_mime_message (ctx, hdr);
 

Attachment: signature.asc
Description: Digital signature