score.c send.c sendlib.c signal.c sort.c \
status.c system.c thread.c charset.c history.c lib.c \
muttlib.c editmsg.c utf8.c mbyte.c wcwidth.c \
- url.c ascii.c
+ url.c ascii.c mutt_idna.c
mutt_LDADD = @MUTT_LIB_OBJECTS@ @LIBOBJS@ $(LIBIMAP) $(MUTTLIBS) \
$(INTLLIBS) $(LIBICONV)
EXTRA_mutt_SOURCES = account.c md5c.c mutt_sasl.c mutt_socket.c mutt_ssl.c \
mutt_tunnel.c pop.c pop_auth.c pop_lib.c smime.c pgp.c pgpinvoke.c pgpkey.c \
pgplib.c sha1.c pgpmicalg.c gnupgparse.c resize.c dotlock.c remailer.c \
- browser.h mbyte.h remailer.h url.h mutt_ssl_nss.c pgppacket.c
+ browser.h mbyte.h remailer.h url.h mutt_ssl_nss.c \
+ pgppacket.c mutt_idna.h
EXTRA_DIST = COPYRIGHT GPL OPS OPS.PGP OPS.CRYPT OPS.SMIME TODO \
configure acconfig.h account.h \
mbyte.h lib.h extlib.c pgpewrap.c smime_keys.pl pgplib.h Muttrc.head Muttrc \
makedoc.c stamp-doc-rc README.SSL smime.h\
muttbug pgppacket.h depcomp ascii.h BEWARE PATCHES patchlist.sh \
- ChangeLog.old mkchangelog.sh cvslog2changelog.pl
+ ChangeLog.old mkchangelog.sh cvslog2changelog.pl mutt_idna.h
mutt_dotlock_SOURCES = mutt_dotlock.c
mutt_dotlock_LDADD = @LIBOBJS@
break;
case 'r':
adr[0] = 0;
- rfc822_write_address (adr, sizeof (adr), alias->addr);
+ rfc822_write_address (adr, sizeof (adr), alias->addr, 1);
snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
snprintf (dest, destlen, tmp, adr);
break;
{
if (AliasTable[i]->tagged)
{
- rfc822_write_address (buf, buflen, AliasTable[i]->addr);
+ rfc822_write_address (buf, buflen, AliasTable[i]->addr, 0);
t = -1;
}
}
if(t != -1)
- rfc822_write_address (buf, buflen, AliasTable[t]->addr);
+ rfc822_write_address (buf, buflen, AliasTable[t]->addr, 0);
mutt_menuDestroy (&menu);
FREE (&AliasTable);
#include "mutt.h"
#include "mutt_regex.h"
#include "mutt_curses.h"
+#include "mutt_idna.h"
#include <string.h>
#include <ctype.h>
{
ALIAS *new, *t;
char buf[LONG_STRING], prompt[SHORT_STRING], *pc;
+ char *err = NULL;
char fixed[LONG_STRING];
FILE *rc;
ADDRESS *adr = NULL;
if((new->addr = rfc822_parse_adrlist (new->addr, buf)) == NULL)
BEEP ();
+ if (mutt_addrlist_to_idna (new->addr, &err))
+ {
+ mutt_error (_("Error: '%s' is a bad IDN."), err);
+ mutt_sleep (2);
+ continue;
+ }
}
while(new->addr == NULL);
new->addr->personal = safe_strdup (buf);
buf[0] = 0;
- rfc822_write_address (buf, sizeof (buf), new->addr);
+ rfc822_write_address (buf, sizeof (buf), new->addr, 1);
snprintf (prompt, sizeof (prompt), _("[%s = %s] Accept?"), new->name, buf);
if (mutt_yesorno (prompt, M_YES) != M_YES)
{
strfcpy (buf, new->name, sizeof (buf));
fprintf (rc, "alias %s ", buf);
buf[0] = 0;
- rfc822_write_address (buf, sizeof (buf), new->addr);
+ rfc822_write_address (buf, sizeof (buf), new->addr, 0);
write_safe_address (rc, buf);
fputc ('\n', rc);
fclose (rc);
void mutt_set_langinfo_charset (void);
#define M_ICONV_HOOK_FROM 1
-#if 0
#define M_ICONV_HOOK_TO 2
-#endif
#endif /* _CHARSET_H */
#include "mx.h"
#include "pager.h"
#include "mutt_crypt.h"
+#include "mutt_idna.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
char prompt[SHORT_STRING];
char buf[HUGE_STRING] = { 0 };
ADDRESS *adr = NULL;
+ char *err = NULL;
int rc;
if(h)
adr = mutt_expand_aliases (adr);
+ if (mutt_addrlist_to_idna (adr, &err) < 0)
+ {
+ mutt_error (_("Bad IDN: '%s'"), err);
+ FREE (&err);
+ rfc822_free_address (&adr);
+ return;
+ }
+
buf[0] = 0;
- rfc822_write_address (buf, sizeof (buf), adr);
+ rfc822_write_address (buf, sizeof (buf), adr, 1);
#define extra_space (15 + 7 + 2)
snprintf (prompt, sizeof (prompt),
adr = mutt_get_address (env, &pfx);
if (!adr) return;
-
+
+ /*
+ * Note: We don't convert IDNA to local representation this time.
+ * That is intentional, so the user has an opportunity to copy &
+ * paste the on-the-wire form of the address to other, IDN-unable
+ * software.
+ */
+
buf[0] = 0;
- rfc822_write_address (buf, sizeof (buf), adr);
+ rfc822_write_address (buf, sizeof (buf), adr, 0);
mutt_message ("%s: %s", pfx, buf);
}
#include "mutt.h"
#include "mutt_curses.h"
+#include "mutt_idna.h"
#include "mutt_menu.h"
#include "rfc1524.h"
#include "mime.h"
char buf[STRING];
buf[0] = 0;
- rfc822_write_address (buf, sizeof (buf), addr);
+ rfc822_write_address (buf, sizeof (buf), addr, 1);
mvprintw (line, 0, TITLE_FMT, Prompts[line - 1]);
mutt_paddstr (W, buf);
}
static int edit_address_list (int line, ADDRESS **addr)
{
char buf[HUGE_STRING] = ""; /* needs to be large for alias expansion */
-
- rfc822_write_address (buf, sizeof (buf), *addr);
+ char *err = NULL;
+
+ mutt_addrlist_to_local (*addr);
+ rfc822_write_address (buf, sizeof (buf), *addr, 0);
if (mutt_get_field (Prompts[line - 1], buf, sizeof (buf), M_ALIAS) == 0)
{
rfc822_free_address (addr);
return (REDRAW_FULL);
}
+ if (mutt_addrlist_to_idna (*addr, &err) != 0)
+ {
+ mutt_error (_("Warning: '%s' is a bad IDN."), err);
+ mutt_refresh();
+ FREE (&err);
+ }
+
/* redraw the expanded list so the user can see the result */
buf[0] = 0;
- rfc822_write_address (buf, sizeof (buf), *addr);
+ rfc822_write_address (buf, sizeof (buf), *addr, 1);
move (line, HDR_XOFFSET);
mutt_paddstr (W, buf);
-
+
return 0;
}
if (op == OP_COMPOSE_EDIT_HEADERS ||
(op == OP_COMPOSE_EDIT_MESSAGE && option (OPTEDITHDRS)))
{
+ char *tag = NULL, *err = NULL;
+ mutt_env_to_local (msg->env);
mutt_edit_headers (NONULL (Editor), msg->content->filename, msg,
fcc, fcclen);
+ if (mutt_env_to_idna (msg->env, &tag, &err))
+ {
+ mutt_error (_("Bad IDN in \"%s\": '%s'"), tag, err);
+ FREE (&err);
+ }
}
else
{
])
AM_CONDITIONAL(USE_SASL, test x$need_sasl = xyes)
+AC_ARG_WITH(idn, [ --with-idn=[PFX] Use GNU libidn for domain names],
+ [
+ if test "$with_idn" != "no" ; then
+ if test "$with_idn" != "yes" ; then
+ CPPFLAGS="$CPPFLAGS -I$with_idn/include"
+ LDFLAGS="$LDFLAGS -L$with_idn/lib"
+ fi
+ fi
+ ]
+)
+
+AC_CHECK_LIB(idn, idna_to_ascii_from_utf8)
+
if test "$need_md5" = "yes"
then
MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS md5c.o"
#include "rfc2047.h"
#include "mime.h"
#include "mutt_crypt.h"
+#include "mutt_idna.h"
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h> /* needed for SEEK_SET under SunOS 4.1.4 */
+static int address_header_decode (char **str);
static int copy_delete_attach (BODY *b, FILE *fpin, FILE *fpout, char *date);
/* Ok, the only reason for not merging this with mutt_copy_header()
char **headers;
int hdr_count;
int x;
+ char *this_one = NULL;
int error;
if (ftell (in) != off_start)
/* Is it the begining of a header? */
if (nl && buf[0] != ' ' && buf[0] != '\t')
{
+ /* Do we have anything pending? */
+ if (this_one)
+ {
+ if (flags & CH_DECODE)
+ {
+ if (!address_header_decode (&this_one))
+ rfc2047_decode (&this_one);
+ }
+
+ if (!headers[x])
+ headers[x] = this_one;
+ else
+ {
+ safe_realloc ((void **) &headers[x], mutt_strlen (headers[x]) +
+ mutt_strlen (this_one) + sizeof (char));
+ strcat (headers[x], this_one); /* __STRCAT_CHECKED__ */
+ FREE (&this_one);
+ }
+
+ this_one = NULL;
+ }
+
ignore = 1;
this_is_from = 0;
if (!from && mutt_strncmp ("From ", buf, 5) == 0)
}
}
}
-
+
ignore = 0;
} /* If beginning of header */
if (!ignore)
{
dprint (2, (debugfile, "Reorder: x = %d; hdr_count = %d\n", x, hdr_count));
- /* Save the header in headers[x] */
- if (!headers[x])
- headers[x] = safe_strdup (buf);
+ if (!this_one)
+ this_one = safe_strdup (buf);
else
{
- safe_realloc ((void **) &headers[x],
- mutt_strlen (headers[x]) + mutt_strlen (buf) + sizeof (char));
- strcat (headers[x], buf); /* __STRCAT_CHECKED__ */
+ safe_realloc ((void **) &this_one,
+ mutt_strlen (this_one) + mutt_strlen (buf) + sizeof (char));
+ strcat (this_one, buf); /* __STRCAT_CHECKED__ */
}
}
} /* while (ftell (in) < off_end) */
+ /* Do we have anything pending? -- XXX, same code as in above in the loop. */
+ if (this_one)
+ {
+ if (flags & CH_DECODE)
+ {
+ if (!address_header_decode (&this_one))
+ rfc2047_decode (&this_one);
+ }
+
+ if (!headers[x])
+ headers[x] = this_one;
+ else
+ {
+ safe_realloc ((void **) &headers[x], mutt_strlen (headers[x]) +
+ mutt_strlen (this_one) + sizeof (char));
+ strcat (headers[x], this_one); /* __STRCAT_CHECKED__ */
+ FREE (&this_one);
+ }
+
+ this_one = NULL;
+ }
+
/* Now output the headers in order */
for (x = 0; x < hdr_count; x++)
{
if (headers[x])
{
+#if 0
if (flags & CH_DECODE)
rfc2047_decode (&headers[x]);
+#endif
/* We couldn't do the prefixing when reading because RFC 2047
* decoding may have concatenated lines.
return 0;
}
+
+static int address_header_decode (char **h)
+{
+ char *s = *h;
+ int l, ll;
+
+ ADDRESS *a = NULL;
+
+ switch (tolower (*s))
+ {
+ case 'r':
+ {
+ if (ascii_strncasecmp (s, "return-path:", 12) == 0)
+ {
+ l = 12;
+ break;
+ }
+ else if (ascii_strncasecmp (s, "reply-to:", 9) == 0)
+ {
+ l = 9;
+ break;
+ }
+ return 0;
+ }
+ case 'f':
+ {
+ if (ascii_strncasecmp (s, "from:", 5))
+ return 0;
+ l = 5;
+ break;
+ }
+ case 'c':
+ {
+ if (ascii_strncasecmp (s, "to:", 3))
+ return 0;
+ l = 3;
+ break;
+
+ }
+ case 'b':
+ {
+ if (ascii_strncasecmp (s, "bcc:", 4))
+ return 0;
+ l = 4;
+ break;
+ }
+ case 's':
+ {
+ if (ascii_strncasecmp (s, "sender:", 7))
+ return 0;
+ l = 7;
+ break;
+ }
+ case 'm':
+ {
+ if (ascii_strncasecmp (s, "mail-followup-to:", 17))
+ return 0;
+ l = 17;
+ break;
+ }
+ default: return 0;
+ }
+
+ if ((a = rfc822_parse_adrlist (a, s + l + 1)) == NULL)
+ return 0;
+
+ mutt_addrlist_to_local (a);
+ rfc2047_decode_adrlist (a);
+
+ ll = mutt_strlen (s) * 6 + 3; /* XXX -- should be safe */
+ *h = safe_calloc (1, ll);
+
+ strncpy (*h, s, l);
+ strncat (*h, " ", ll);
+ rfc822_write_address (*h + l + 1, ll - l - 1, a, 0);
+ rfc822_free_address (&a);
+
+ strncat (*h, "\n", ll);
+
+ safe_realloc ((void **) h, mutt_strlen (*h) + 1);
+
+ FREE (&s);
+ return 1;
+}
#include "mutt.h"
#include "mutt_curses.h"
+#include "mutt_idna.h"
#include <stdio.h>
#include <string.h>
{
addstr ("To: ");
tmp[0] = 0;
- rfc822_write_address (tmp, sizeof (tmp), env->to);
+ rfc822_write_address (tmp, sizeof (tmp), env->to, 1);
addstr (tmp);
addch ('\n');
}
{
addstr ("Cc: ");
tmp[0] = 0;
- rfc822_write_address (tmp, sizeof (tmp), env->cc);
+ rfc822_write_address (tmp, sizeof (tmp), env->cc, 1);
addstr (tmp);
addch ('\n');
}
{
addstr ("Bcc: ");
tmp[0] = 0;
- rfc822_write_address (tmp, sizeof (tmp), env->bcc);
+ rfc822_write_address (tmp, sizeof (tmp), env->bcc, 1);
addstr (tmp);
addch ('\n');
}
addstr ("To: ");
tmp[0] = 0;
- rfc822_write_address (tmp, sizeof (tmp), e->to);
+ mutt_addrlist_to_local (e->to);
+ rfc822_write_address (tmp, sizeof (tmp), e->to, 0);
if (!e->to || force)
{
if (mutt_enter_string (tmp, sizeof (tmp), LINES-1, 4, 0) == 0)
rfc822_free_address (&e->to);
e->to = mutt_parse_adrlist (e->to, tmp);
e->to = mutt_expand_aliases (e->to);
+ mutt_addrlist_to_idna (e->to, NULL); /* XXX - IDNA error reporting? */
tmp[0] = 0;
- rfc822_write_address (tmp, sizeof (tmp), e->to);
+ rfc822_write_address (tmp, sizeof (tmp), e->to, 1);
mvaddstr (LINES - 1, 4, tmp);
}
}
else
{
+ mutt_addrlist_to_idna (e->to, NULL); /* XXX - IDNA error reporting? */
addstr (tmp);
}
addch ('\n');
{
addstr ("Cc: ");
tmp[0] = 0;
- rfc822_write_address (tmp, sizeof (tmp), e->cc);
+ mutt_addrlist_to_local (e->cc);
+ rfc822_write_address (tmp, sizeof (tmp), e->cc, 0);
if (mutt_enter_string (tmp, sizeof (tmp), LINES-1, 4, 0) == 0)
{
rfc822_free_address (&e->cc);
e->cc = mutt_parse_adrlist (e->cc, tmp);
e->cc = mutt_expand_aliases (e->cc);
tmp[0] = 0;
- rfc822_write_address (tmp, sizeof (tmp), e->cc);
+ mutt_addrlist_to_idna (e->cc, NULL);
+ rfc822_write_address (tmp, sizeof (tmp), e->cc, 1);
mvaddstr (LINES - 1, 4, tmp);
}
+ else
+ mutt_addrlist_to_idna (e->cc, NULL);
addch ('\n');
}
{
addstr ("Bcc: ");
tmp[0] = 0;
- rfc822_write_address (tmp, sizeof (tmp), e->bcc);
+ mutt_addrlist_to_local (e->bcc);
+ rfc822_write_address (tmp, sizeof (tmp), e->bcc, 0);
if (mutt_enter_string (tmp, sizeof (tmp), LINES-1, 5, 0) == 0)
{
rfc822_free_address (&e->bcc);
e->bcc = mutt_parse_adrlist (e->bcc, tmp);
e->bcc = mutt_expand_aliases (e->bcc);
+ mutt_addrlist_to_idna (e->bcc, NULL);
tmp[0] = 0;
- rfc822_write_address (tmp, sizeof (tmp), e->bcc);
+ rfc822_write_address (tmp, sizeof (tmp), e->bcc, 1);
mvaddstr (LINES - 1, 5, tmp);
}
+ else
+ mutt_addrlist_to_idna (e->bcc, NULL);
addch ('\n');
}
}
case 'v':
if (be_barf_file (path, buf, buflen) == 0)
{
+ char *tag, *err;
be_free_memory (buf, buflen);
buf = NULL;
bufmax = buflen = 0;
if (option (OPTEDITHDRS))
+ {
+ mutt_env_to_local (msg->env);
mutt_edit_headers (NONULL(Visual), path, msg, NULL, 0);
+ if (mutt_env_to_idna (msg->env, &tag, &err))
+ printw (_("Bad IDN in %s: '%s'\n"), tag, err);
+ }
else
mutt_edit_file (NONULL(Visual), path);
#include "sort.h"
#include "charset.h"
#include "mutt_crypt.h"
+#include "mutt_idna.h"
#include <ctype.h>
#include <stdlib.h>
case 'A':
if(hdr->env->reply_to && hdr->env->reply_to->mailbox)
{
- mutt_format_s (dest, destlen, prefix, hdr->env->reply_to->mailbox);
+ mutt_format_s (dest, destlen, prefix, mutt_addr_for_display (hdr->env->reply_to));
break;
}
/* fall through if 'A' returns nothing */
case 'a':
if(hdr->env->from && hdr->env->from->mailbox)
{
- mutt_format_s (dest, destlen, prefix, hdr->env->from->mailbox);
+ mutt_format_s (dest, destlen, prefix, mutt_addr_for_display (hdr->env->from));
}
else
dest[0] = '\0';
case 'f':
buf2[0] = 0;
- rfc822_write_address (buf2, sizeof (buf2), hdr->env->from);
+ rfc822_write_address (buf2, sizeof (buf2), hdr->env->from, 1);
mutt_format_s (dest, destlen, prefix, buf2);
break;
case 'u':
if (hdr->env->from && hdr->env->from->mailbox)
{
- strfcpy (buf2, hdr->env->from->mailbox, sizeof (buf2));
+ strfcpy (buf2, mutt_addr_for_display (hdr->env->from), sizeof (buf2));
if ((p = strpbrk (buf2, "%@")))
*p = 0;
}
*/
#include "mutt.h"
-
#include "mutt_crypt.h"
+#include "mutt_idna.h"
#include <sys/stat.h>
#include <string.h>
mutt_perror (path);
return;
}
-
+
+ mutt_env_to_local (msg->env);
mutt_write_rfc822_header (ofp, msg->env, NULL, 1, 0);
fputc ('\n', ofp); /* tie off the header. */
#include "mbyte.h"
#include "charset.h"
#include "mutt_crypt.h"
+#include "mutt_idna.h"
#if defined(USE_SSL) || defined(USE_NSS)
#include "mutt_ssl.h"
{
ALIAS *tmp = Aliases;
ALIAS *last = NULL;
-
+ char *estr = NULL;
+
if (!MoreArgs (s))
{
strfcpy (err->data, _("alias: no address"), err->dsize);
last->next = tmp;
else
Aliases = tmp;
+ if (mutt_addrlist_to_idna (tmp->addr, &estr))
+ {
+ snprintf (err->data, err->dsize, _("Warning: Bad IDN '%s' in alias '%s'.\n"),
+ estr, tmp->name);
+ return -1;
+ }
return 0;
}
{
char tmp[HUGE_STRING];
*tmp = '\0';
- rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data));
+ rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
p->init = (unsigned long) safe_strdup (tmp);
}
break;
if (DTYPE (MuttVars[idx].type) == DT_ADDR)
{
_tmp[0] = '\0';
- rfc822_write_address (_tmp, sizeof (_tmp), *((ADDRESS **) MuttVars[idx].data));
+ rfc822_write_address (_tmp, sizeof (_tmp), *((ADDRESS **) MuttVars[idx].data), 0);
val = _tmp;
}
else
}
else if (DTYPE (MuttVars[idx].type) == DT_ADDR)
{
- rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) MuttVars[idx].data));
+ rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) MuttVars[idx].data), 0);
}
else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
#include "mailbox.h"
#include "url.h"
#include "mutt_crypt.h"
+#include "mutt_idna.h"
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#endif
+#ifdef HAVE_LIBIDN
+#include <stringprep.h>
+#endif
+
static const char *ReachingUs = N_("\
To contact the developers, please mail to <mutt-dev@mutt.org>.\n\
To report a bug, please use the flea(1) utility.\n");
printf (" [using libiconv %d.%d]", _LIBICONV_VERSION >> 8,
_LIBICONV_VERSION & 0xff);
#endif
+
+#ifdef HAVE_LIBIDN
+ printf (" [using libidn %s (compiled with %s)]", stringprep_check_version (NULL),
+ STRINGPREP_VERSION);
+#endif
puts (_("\nCompile options:"));
"-ICONV_NONTRANS "
#endif
+#if HAVE_LIBIDN
+ "+HAVE_LIBIDN "
+#else
+ "-HAVE_LIBIDN "
+#endif
+
#if HAVE_GETSID
"+HAVE_GETSID "
#else
for (; alias_queries; alias_queries = alias_queries->next)
{
if ((a = mutt_lookup_alias (alias_queries->data)))
- mutt_write_address_list (a, stdout, 0);
+ {
+ /* output in machine-readable form */
+ mutt_addrlist_to_idna (a, NULL);
+ mutt_write_address_list (a, stdout, 0, 0);
+ }
else
{
rv = 1;
--- /dev/null
+/*
+ * Copyright (C) 2003 Thomas Roessler <roessler@does-not-exist.org>
+ *
+ * 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 "config.h"
+#include "mutt.h"
+#include "charset.h"
+#include "mutt_idna.h"
+
+/* The low-level interface we use. */
+
+#ifndef HAVE_LIBIDN
+
+int mutt_idna_to_local (const char *in, char **out, int flags)
+{
+ *out = safe_strdup (in);
+ return 1;
+}
+
+int mutt_local_to_idna (const char *in, char **out)
+{
+ *out = safe_strdup (in);
+ return 0;
+}
+
+#else
+
+int mutt_idna_to_local (const char *in, char **out, int flags)
+{
+ *out = NULL;
+
+ /* Is this the right function? Interesting effects with some bad identifiers! */
+ if (idna_to_unicode_utf8_from_utf8 (in, out, 0, 1) != IDNA_SUCCESS)
+ goto notrans;
+ if (mutt_convert_string (out, "utf-8", Charset, M_ICONV_HOOK_TO) == -1)
+ goto notrans;
+
+ /*
+ * make sure that we can convert back and come out with the same
+ * domain name. */
+
+ if ((flags & MI_MAY_BE_IRREVERSIBLE) == 0)
+ {
+ int irrev = 0;
+ char *t2 = NULL;
+ char *tmp = safe_strdup (*out);
+ if (mutt_convert_string (&tmp, Charset, "utf-8", M_ICONV_HOOK_FROM) == -1)
+ irrev = 1;
+ if (!irrev && idna_to_ascii_from_utf8 (tmp, &t2, 0, 1) != IDNA_SUCCESS)
+ irrev = 1;
+ if (!irrev && ascii_strcasecmp (t2, in))
+ {
+ dprint (1, (debugfile, "mutt_idna_to_local: Not reversible. in = '%s', t2 = '%s'.\n",
+ in, t2));
+ irrev = 1;
+ }
+
+ FREE (&t2);
+ FREE (&tmp);
+
+ if (irrev)
+ goto notrans;
+ }
+
+ return 0;
+
+ notrans:
+ FREE (out);
+ *out = safe_strdup (in);
+ return 1;
+}
+
+int mutt_local_to_idna (const char *in, char **out)
+{
+ int rv = 0;
+ char *tmp = safe_strdup (in);
+ *out = NULL;
+
+ if (mutt_convert_string (&tmp, Charset, "utf-8", M_ICONV_HOOK_FROM) == -1)
+ rv = -1;
+ if (!rv && idna_to_ascii_from_utf8 (tmp, out, 0, 1) != IDNA_SUCCESS)
+ rv = -2;
+
+ FREE (&tmp);
+ if (rv < 0)
+ {
+ FREE (out);
+ *out = safe_strdup (in);
+ }
+ return rv;
+}
+
+#endif
+
+
+/* higher level functions */
+
+static int mbox_to_udomain (const char *mbx, char **user, char **domain)
+{
+ char *scratch = safe_strdup (mbx);
+ *user = NULL;
+ *domain = NULL;
+
+ if ((*domain = strchr (scratch, '@')) == NULL)
+ return -1;
+
+ **domain = '\0';
+ *domain = safe_strdup (*domain + 1);
+ *user = scratch;
+ return 0;
+}
+
+int mutt_addrlist_to_idna (ADDRESS *a, char **err)
+{
+ char *user = NULL, *domain = NULL;
+ char *tmp = NULL;
+ int e = 0;
+
+ if (err)
+ *err = NULL;
+
+ for (; a; a = a->next)
+ {
+ if (!a->mailbox)
+ continue;
+ if (mbox_to_udomain (a->mailbox, &user, &domain) == -1)
+ continue;
+
+ if (mutt_local_to_idna (domain, &tmp) < 0)
+ {
+ e = 1;
+ if (err)
+ *err = safe_strdup (domain);
+ }
+ else
+ {
+ safe_realloc ((void **) &a->mailbox, mutt_strlen (user) + mutt_strlen (tmp) + 2);
+ sprintf (a->mailbox, "%s@%s", NONULL(user), NONULL(tmp)); /* __SPRINTF_CHECKED__ */
+ }
+
+ FREE (&domain);
+ FREE (&user);
+ FREE (&tmp);
+
+ if (e)
+ return -1;
+ }
+
+ return 0;
+}
+
+int mutt_addrlist_to_local (ADDRESS *a)
+{
+ char *user, *domain;
+ char *tmp = NULL;
+
+ for (; a; a = a->next)
+ {
+ if (!a->mailbox)
+ continue;
+ if (mbox_to_udomain (a->mailbox, &user, &domain) == -1)
+ continue;
+
+ if (mutt_idna_to_local (domain, &tmp, 0) == 0)
+ {
+ safe_realloc ((void **) &a->mailbox, mutt_strlen (user) + mutt_strlen (tmp) + 2);
+ sprintf (a->mailbox, "%s@%s", NONULL (user), NONULL (tmp)); /* __SPRINTF_CHECKED__ */
+ }
+
+ FREE (&domain);
+ FREE (&user);
+ FREE (&tmp);
+ }
+
+ return 0;
+}
+
+/* convert just for displaying purposes */
+const char *mutt_addr_for_display (ADDRESS *a)
+{
+ static char *buff = NULL;
+ char *tmp = NULL;
+ char *domain, *user;
+
+ FREE (&buff);
+
+ if (mbox_to_udomain (a->mailbox, &user, &domain) != 0)
+ return a->mailbox;
+ if (mutt_idna_to_local (domain, &tmp, MI_MAY_BE_IRREVERSIBLE) != 0)
+ {
+ FREE (&tmp);
+ return a->mailbox;
+ }
+
+ safe_realloc ((void **) &buff, mutt_strlen (tmp) + mutt_strlen (user) + 2);
+ sprintf (buff, "%s@%s", NONULL(user), NONULL(tmp)); /* __SPRINTF_CHECKED__ */
+ FREE (&tmp);
+ return buff;
+}
+
+/* Convert an ENVELOPE structure */
+
+void mutt_env_to_local (ENVELOPE *e)
+{
+ mutt_addrlist_to_local (e->return_path);
+ mutt_addrlist_to_local (e->from);
+ mutt_addrlist_to_local (e->to);
+ mutt_addrlist_to_local (e->cc);
+ mutt_addrlist_to_local (e->bcc);
+ mutt_addrlist_to_local (e->reply_to);
+ mutt_addrlist_to_local (e->mail_followup_to);
+}
+
+/*
+ * XXX -- this macro produces warnings, but yields the right kind
+ * of code when preprocessed. How to fix this *without* typing the same
+ * code all over the place?
+ */
+
+#define H_TO_IDNA(a) \
+ if (mutt_addrlist_to_idna (env->##a, err) && !e) \
+ { \
+ if (tag) *tag = #a; e = 1; err = NULL; \
+ }
+
+int mutt_env_to_idna (ENVELOPE *env, char **tag, char **err)
+{
+ int e = 0;
+ H_TO_IDNA(return_path);
+ H_TO_IDNA(from);
+ H_TO_IDNA(to);
+ H_TO_IDNA(cc);
+ H_TO_IDNA(bcc);
+ H_TO_IDNA(reply_to);
+ H_TO_IDNA(mail_followup_to);
+ return e;
+}
+
+#undef H_TO_IDNA
--- /dev/null
+/*
+ * Copyright (C) 2003 Thomas Roessler <roessler@does-not-exist.org>
+ *
+ * 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.
+ */
+
+#ifndef _MUTT_IDNA_H
+# define _MUTT_IDNA_H
+
+#include "config.h"
+#include "rfc822.h"
+#include "charset.h"
+
+#ifdef HAVE_LIBIDN
+#include <idna.h>
+#endif
+
+#define MI_MAY_BE_IRREVERSIBLE (1 << 0)
+
+int mutt_idna_to_local (const char *, char **, int);
+int mutt_local_to_idna (const char *, char **);
+
+int mutt_addrlist_to_idna (ADDRESS *, char **);
+int mutt_addrlist_to_local (ADDRESS *);
+
+void mutt_env_to_local (ENVELOPE *);
+int mutt_env_to_idna (ENVELOPE *, char **, char **);
+
+const char *mutt_addr_for_display (ADDRESS *a);
+
+#endif
#include "mutt.h"
#include "mutt_curses.h"
+#include "mutt_idna.h"
#include "pgp.h"
#include "rfc822.h"
addr->personal = NULL;
*tmp = '\0';
- rfc822_write_address_single (tmp, sizeof (tmp), addr);
+ mutt_addrlist_to_local (addr);
+ rfc822_write_address_single (tmp, sizeof (tmp), addr, 0);
mutt_quote_filename (buff, sizeof (buff), tmp);
addr->personal = personal;
void mutt_update_tree (ATTACHPTR **, short);
void mutt_version (void);
void mutt_view_attachments (HEADER *);
-void mutt_write_address_list (ADDRESS *adr, FILE *fp, int linelen);
+void mutt_write_address_list (ADDRESS *adr, FILE *fp, int linelen, int display);
void mutt_set_virtual (CONTEXT *);
int mutt_addr_is_user (ADDRESS *);
#include "mutt.h"
#include "mutt_menu.h"
+#include "mutt_idna.h"
#include "mapping.h"
#include "sort.h"
if(!tmp.next && !tmp.personal)
tmp.personal = r->name;
+ mutt_addrlist_to_idna (&tmp, NULL);
return &tmp;
}
SecondColumn = QUERY_MIN_COLUMN_LENGHT;
}
- rfc822_write_address (buf, sizeof (buf), table[num].data->addr);
+ rfc822_write_address (buf, sizeof (buf), table[num].data->addr, 1);
mutt_format_string (buf2, sizeof (buf2),
FirstColumn + 2, FirstColumn + 2,
int mutt_query_complete (char *buf, size_t buflen)
{
QUERY *results = NULL;
+ ADDRESS *tmpa;
if (!QueryCmd)
{
/* only one response? */
if (results->next == NULL)
{
+ tmpa = result_to_addr (results);
+ mutt_addrlist_to_local (tmpa);
buf[0] = '\0';
- rfc822_write_address (buf, buflen, result_to_addr(results));
+ rfc822_write_address (buf, buflen, tmpa, 0);
mutt_clear_error ();
return (0);
}
{
if (curpos == 0)
{
+ ADDRESS *tmpa = result_to_addr (QueryTable[i].data);
+ mutt_addrlist_to_local (tmpa);
tagged = 1;
- rfc822_write_address (buf, buflen, result_to_addr(QueryTable[i].data));
+ rfc822_write_address (buf, buflen, tmpa, 0);
curpos = mutt_strlen (buf);
}
else if (curpos + 2 < buflen)
{
+ ADDRESS *tmpa = result_to_addr (QueryTable[i].data);
+ mutt_addrlist_to_local (tmpa);
strcat (buf, ", "); /* __STRCAT_CHECKED__ */
rfc822_write_address ((char *) buf + curpos + 1, buflen - curpos - 1,
- result_to_addr(QueryTable[i].data));
+ tmpa, 0);
curpos = mutt_strlen (buf);
}
}
/* then enter current message */
if (!tagged)
{
- rfc822_write_address (buf, buflen, result_to_addr(QueryTable[menu->current].data));
+ ADDRESS *tmpa = result_to_addr (QueryTable[menu->current].data);
+ mutt_addrlist_to_local (tmpa);
+ rfc822_write_address (buf, buflen, tmpa, 0);
}
}
#include "mapping.h"
#include "mx.h"
#include "copy.h"
-
+#include "mutt_idna.h"
/* some helper functions to verify that we are exclusively operating
* on message/rfc822 attachments
short i;
char prompt[STRING];
char buf[HUGE_STRING];
+ char *err = NULL;
ADDRESS *adr = NULL;
int ret = 0;
int p = 0;
}
adr = mutt_expand_aliases (adr);
+
+ if (mutt_addrlist_to_idna (adr, &err) < 0)
+ {
+ mutt_error (_("Bad IDN: '%s'"), err);
+ FREE (&err);
+ rfc822_free_address (&adr);
+ return;
+ }
+
buf[0] = 0;
- rfc822_write_address (buf, sizeof (buf), adr);
+ rfc822_write_address (buf, sizeof (buf), adr, 1);
#define extra_space (15+7+2)
/*
#include "rfc822.h"
#endif
+#include "mutt_idna.h"
+
#define terminate_string(a, b, c) do { if ((b) < (c)) a[(b)] = 0; else \
a[(c)] = 0; } while (0)
strfcpy (buf, value, buflen);
}
-void rfc822_write_address_single (char *buf, size_t buflen, ADDRESS *addr)
+void rfc822_write_address_single (char *buf, size_t buflen, ADDRESS *addr,
+ int display)
{
size_t len;
char *pbuf = buf;
{
if (!buflen)
goto done;
- if (ascii_strcmp (addr->mailbox, "@"))
+ if (ascii_strcmp (addr->mailbox, "@") && !display)
{
strfcpy (pbuf, addr->mailbox, buflen);
len = mutt_strlen (pbuf);
}
+ else if (ascii_strcmp (addr->mailbox, "@") && display)
+ {
+ strfcpy (pbuf, mutt_addr_for_display (addr), buflen);
+ len = mutt_strlen (pbuf);
+ }
else
{
*pbuf = '\0';
}
/* note: it is assumed that `buf' is nul terminated! */
-void rfc822_write_address (char *buf, size_t buflen, ADDRESS *addr)
+void rfc822_write_address (char *buf, size_t buflen, ADDRESS *addr, int display)
{
char *pbuf = buf;
size_t len = mutt_strlen (buf);
{
/* use buflen+1 here because we already saved space for the trailing
nul char, and the subroutine can make use of it */
- rfc822_write_address_single (pbuf, buflen + 1, addr);
+ rfc822_write_address_single (pbuf, buflen + 1, addr, display);
/* this should be safe since we always have at least 1 char passed into
the above call, which means `pbuf' should always be nul terminated */
ADDRESS *rfc822_cpy_adr (ADDRESS *addr);
ADDRESS *rfc822_cpy_adr_real (ADDRESS *addr);
ADDRESS *rfc822_append (ADDRESS **a, ADDRESS *b);
-void rfc822_write_address (char *, size_t, ADDRESS *);
-void rfc822_write_address_single (char *, size_t, ADDRESS *);
-void rfc822_write_list (char *, size_t, ADDRESS *);
+void rfc822_write_address (char *, size_t, ADDRESS *, int);
+void rfc822_write_address_single (char *, size_t, ADDRESS *, int);
void rfc822_free_address (ADDRESS **addr);
void rfc822_cat (char *, size_t, const char *, const char *);
#include "copy.h"
#include "mx.h"
#include "mutt_crypt.h"
+#include "mutt_idna.h"
#include <ctype.h>
#include <stdlib.h>
static int edit_address (ADDRESS **a, /* const */ char *field)
{
char buf[HUGE_STRING];
-
- buf[0] = 0;
- rfc822_write_address (buf, sizeof (buf), *a);
- if (mutt_get_field (field, buf, sizeof (buf), M_ALIAS) != 0)
- return (-1);
- rfc822_free_address (a);
- *a = mutt_expand_aliases (mutt_parse_adrlist (NULL, buf));
+ char *err = NULL;
+ int idna_ok = 0;
+
+ do
+ {
+ buf[0] = 0;
+ mutt_addrlist_to_local (*a);
+ rfc822_write_address (buf, sizeof (buf), *a, 0);
+ if (mutt_get_field (field, buf, sizeof (buf), M_ALIAS) != 0)
+ return (-1);
+ rfc822_free_address (a);
+ *a = mutt_expand_aliases (mutt_parse_adrlist (NULL, buf));
+ if ((idna_ok = mutt_addrlist_to_idna (*a, &err)) != 0)
+ {
+ mutt_error (_("Error: '%s' is a bad IDN."), err);
+ mutt_refresh ();
+ mutt_sleep (2);
+ FREE (&err);
+ }
+ }
+ while (idna_ok != 0);
return 0;
}
fputs ("----- Forwarded message from ", fp);
buffer[0] = 0;
- rfc822_write_address (buffer, sizeof (buffer), cur->env->from);
+ rfc822_write_address (buffer, sizeof (buffer), cur->env->from, 1);
fputs (buffer, fp);
fputs (" -----\n\n", fp);
}
cmflags |= M_CM_NOHEADER;
if (option (OPTWEED))
{
- chflags |= CH_WEED;
+ chflags |= CH_WEED | CH_REORDER;
cmflags |= M_CM_WEED;
}
char *pgpkeylist = NULL;
/* save current value of "pgp_sign_as" */
char *signas = NULL;
+ char *tag = NULL, *err = NULL;
int rv = -1;
else if (!Editor || mutt_strcmp ("builtin", Editor) == 0)
mutt_builtin_editor (msg->content->filename, msg, cur);
else if (option (OPTEDITHDRS))
+ {
+ mutt_env_to_local (msg->env);
mutt_edit_headers (Editor, msg->content->filename, msg, fcc, sizeof (fcc));
+ mutt_env_to_idna (msg->env, NULL, NULL);
+ }
else
mutt_edit_file (Editor, msg->content->filename);
}
encode_descriptions (msg->content, 1);
mutt_prepare_envelope (msg->env, 0);
+ mutt_env_to_idna (msg->env, NULL, NULL); /* Handle bad IDNAs the next time. */
if (!Postponed || mutt_write_fcc (NONULL (Postponed), msg, (cur && (flags & SENDREPLY)) ? cur->env->message_id : NULL, 1, fcc) < 0)
{
}
}
+ if (mutt_env_to_idna (msg->env, &tag, &err))
+ {
+ mutt_error (_("Bad IDN in \"%s\": '%s'"), tag, err);
+ FREE (&err);
+ goto main_loop;
+ }
+
if (!msg->env->subject && ! (flags & SENDBATCH) &&
(i = query_quadoption (OPT_SUBJECT, _("No subject, abort sending?"))) != M_NO)
{
#include "pager.h"
#include "charset.h"
#include "mutt_crypt.h"
+#include "mutt_idna.h"
#include <string.h>
#include <stdlib.h>
/* wrapper around mutt_write_address() so we can handle very large
recipient lists without needing a huge temporary buffer in memory */
-void mutt_write_address_list (ADDRESS *adr, FILE *fp, int linelen)
+void mutt_write_address_list (ADDRESS *adr, FILE *fp, int linelen, int display)
{
ADDRESS *tmp;
char buf[LONG_STRING];
tmp = adr->next;
adr->next = NULL;
buf[0] = 0;
- rfc822_write_address (buf, sizeof (buf), adr);
+ rfc822_write_address (buf, sizeof (buf), adr, display);
len = mutt_strlen (buf);
if (count && linelen + len > 74)
{
/* Note: all RFC2047 encoding should be done outside of this routine, except
* for the "real name." This will allow this routine to be used more than
* once, if necessary.
+ *
+ * Likewise, all IDN processing should happen outside of this routine.
*
* mode == 1 => "lite" mode (used for edit_hdrs)
* mode == 0 => normal mode. write full header + MIME headers
* privacy != 0 => will omit any headers which may identify the user.
* Output generated is suitable for being sent through
* anonymous remailer chains.
- *
+ *
*/
int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach,
if (env->from && !privacy)
{
buffer[0] = 0;
- rfc822_write_address (buffer, sizeof (buffer), env->from);
+ rfc822_write_address (buffer, sizeof (buffer), env->from, 0);
fprintf (fp, "From: %s\n", buffer);
}
if (env->to)
{
fputs ("To: ", fp);
- mutt_write_address_list (env->to, fp, 4);
+ mutt_write_address_list (env->to, fp, 4, 0);
}
else if (mode > 0)
fputs ("To: \n", fp);
if (env->cc)
{
fputs ("Cc: ", fp);
- mutt_write_address_list (env->cc, fp, 4);
+ mutt_write_address_list (env->cc, fp, 4, 0);
}
else if (mode > 0)
fputs ("Cc: \n", fp);
if(mode != 0 || option(OPTWRITEBCC))
{
fputs ("Bcc: ", fp);
- mutt_write_address_list (env->bcc, fp, 5);
+ mutt_write_address_list (env->bcc, fp, 5, 0);
}
}
else if (mode > 0)
if (env->reply_to)
{
fputs ("Reply-To: ", fp);
- mutt_write_address_list (env->reply_to, fp, 10);
+ mutt_write_address_list (env->reply_to, fp, 10, 0);
}
else if (mode > 0)
fputs ("Reply-To: \n", fp);
if (env->mail_followup_to)
{
fputs ("Mail-Followup-To: ", fp);
- mutt_write_address_list (env->mail_followup_to, fp, 18);
+ mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
}
if (mode <= 0)
fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof(date)));
fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid());
fputs ("Resent-To: ", f);
- mutt_write_address_list (to, f, 11);
+ mutt_write_address_list (to, f, 11, 0);
mutt_copy_header (fp, h, f, ch_flags, NULL);
fputc ('\n', f);
mutt_copy_bytes (fp, f, h->content->length);
const char *fqdn = mutt_fqdn (1);
char resent_from[STRING];
int ret;
-
+ char *err;
+
resent_from[0] = '\0';
from = mutt_default_from ();
rfc822_qualify (from, fqdn);
rfc2047_encode_adrlist (from, "Resent-From");
-
- rfc822_write_address (resent_from, sizeof (resent_from), from);
+ if (mutt_addrlist_to_idna (from, &err))
+ {
+ mutt_error (_("Bad IDN %s while preparing resent-from."),
+ err);
+ return -1;
+ }
+ rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
ret = _mutt_bounce_message (fp, h, to, resent_from, from);