Hi, * Rocco Rutte wrote:
Thoughts?
Sorry for spamming the list with patches, but I've made some cleanup and put a user of mutt_check_charset() in place in init.c.
In case an option name contains "charset" it does assume a colon-delimited string and checks it. In case the new value is wrong, the old is kept and it bails out. In theory... :)
I didn't yet check if this does produce an error when reading config files prior to the 'Bad IDN' error messages (since this comes up every now and then but I can't imagine that many people mess with charset config themselves, but...)
Rocco
comparing with ../pdmef/feature/chs-validation searching for changes diff --git a/charset.c b/charset.c --- a/charset.c +++ b/charset.c @@ -245,7 +245,7 @@ void mutt_canonical_charset (char *dest, char *p; char scratch[LONG_STRING]; - if (!ascii_strcasecmp (name, "utf-8")) + if (!ascii_strcasecmp (name, "utf-8") || !ascii_strcasecmp (name, "utf8")) { strfcpy (dest, "utf-8", dlen); return; @@ -629,3 +629,61 @@ void fgetconv_close (FGETCONV **_fc) iconv_close (fc->cd); FREE (_fc); /* __FREE_CHECKED__ */ } + +#ifdef HAVE_ICONV_LIST + +struct iconvlist_data +{ + const char *name; + int found; +}; + +static int check_charset_cb (unsigned int cnt, const char * const *names, void *data) +{ + struct iconvlist_data *d = (struct iconvlist_data *)data; + unsigned int i; + + for (i = 0; !d->found && i < cnt; i++) + if (ascii_strcasecmp (names[i], d->name) == 0) + d->found = 1; + return d->found; +} + +#endif /* HAVE_ICONV_LIST */ + +int mutt_check_charset (const char *s, int strict) +{ + int i; +#if HAVE_ICONV_LIST + struct iconvlist_data data; +#else + iconv_t cd; +#endif + + if (mutt_is_utf8 (s)) + return 0; + + if (!strict) + for (i = 0; PreferredMIMENames[i].key; i++) + { + if (ascii_strcasecmp (PreferredMIMENames[i].key, s) == 0 || + ascii_strcasecmp (PreferredMIMENames[i].pref, s) == 0) + return 0; + } + +#if HAVE_ICONV_LIST + data.name = s; + data.found = 0; + iconvlist (check_charset_cb, &data); + if (data.found) + return 0; +#else + if ((cd = mutt_iconv_open (s, "utf-8", 0)) != (iconv_t)(-1)) + { + iconv_close (cd); + return 0; + } +#endif + + return -1; +} diff --git a/charset.h b/charset.h --- a/charset.h +++ b/charset.h @@ -56,4 +56,11 @@ char *mutt_get_default_charset (); */ #define M_ICONV_HOOK_FROM 1 /* apply charset-hooks to fromcode */ +/* Check if given character set is valid (either officially assigned by + * known to local iconv() implementation). If strict is non-zero, check + * against iconv() only. Returns 0 if known and negative otherwise. + * Uses mutt_iconv_open() if iconvlist() is missing. + */ +int mutt_check_charset (const char *s, int strict); + #endif /* _CHARSET_H */ diff --git a/configure.ac b/configure.ac --- a/configure.ac +++ b/configure.ac @@ -982,6 +982,35 @@ AC_CHECK_HEADERS(iconv.h, [Define if <iconv.h> defines iconv_t.])], AC_MSG_RESULT(no))]) +AC_CACHE_CHECK([for iconvlist()], mutt_cv_iconv_list, + mutt_save_LIBS="$LIBS" + LIBS="$LIBS $LIBICONV" + AC_TRY_RUN([ +#include <iconv.h> + +static int count = 0; + +static int test_iconvlist (unsigned int c, const char **n, void *d) +{ + count += c; + return 0; +} + +int main() +{ + iconvlist (test_iconvlist, 0); + return count > 0 ? 0 : 1; +} + ], + mutt_cv_iconv_list=yes, + mutt_cv_iconv_list=no, + mutt_cv_iconv_list=yes) + LIBS="$mutt_save_LIBS") +if test "$mutt_cv_iconv_list" = yes; then + AC_DEFINE(HAVE_ICONV_LIST,1, [Define if you have the iconvlist() function.]) +fi + + dnl (1) Some implementations of iconv won't convert from UTF-8 to UTF-8. dnl (2) In glibc-2.1.2 and earlier there is a bug that messes up ob and dnl obl when args 2 and 3 are 0 (fixed in glibc-2.1.3). diff --git a/init.c b/init.c --- a/init.c +++ b/init.c @@ -1684,6 +1684,26 @@ static void pretty_var (char *dst, size_ *p = 0; } +static int check_charset (struct option_t *opt, const char *val) +{ + char *p, *q, *s = safe_strdup (val); + int rc = 0, strict = strcmp (opt->option, "send_charset") == 0; + + for (p = strtok_r (s, ":", &q); p; p = strtok_r (NULL, ":", &q)) + { + if (!*p) + continue; + if (mutt_check_charset (p, strict) < 0) + { + rc = -1; + break; + } + } + + FREE(&s); + return rc; +} + static int parse_set (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err) { int query, unset, inv, reset, r = 0; @@ -1877,14 +1897,9 @@ static int parse_set (BUFFER *tmp, BUFFE myvar = safe_strdup (myvar); myvar_del (myvar); } - else if (DTYPE (MuttVars[idx].type) == DT_ADDR) - rfc822_free_address ((ADDRESS **) MuttVars[idx].data); - else - /* MuttVars[idx].data is already 'char**' (or some 'void**') or... - * so cast to 'void*' is okay */ - FREE ((void *) MuttVars[idx].data); /* __FREE_CHECKED__ */ mutt_extract_token (tmp, s, 0); + if (myvar) { myvar_set (myvar, tmp->data); @@ -1893,18 +1908,32 @@ static int parse_set (BUFFER *tmp, BUFFE } else if (DTYPE (MuttVars[idx].type) == DT_PATH) { + /* MuttVars[idx].data is already 'char**' (or some 'void**') or... + * so cast to 'void*' is okay */ + FREE ((void *) MuttVars[idx].data); /* __FREE_CHECKED__ */ + strfcpy (scratch, tmp->data, sizeof (scratch)); mutt_expand_path (scratch, sizeof (scratch)); *((char **) MuttVars[idx].data) = safe_strdup (scratch); } else if (DTYPE (MuttVars[idx].type) == DT_STR) { + if (strstr (MuttVars[idx].option, "charset") && + check_charset (&MuttVars[idx], tmp->data) < 0) + { + snprintf (err->data, err->dsize, _("Invalid value for option %s: \"%s\""), + MuttVars[idx].option, tmp->data); + return (-1); + } + + FREE ((void *) MuttVars[idx].data); /* __FREE_CHECKED__ */ *((char **) MuttVars[idx].data) = safe_strdup (tmp->data); if (mutt_strcmp (MuttVars[idx].option, "charset") == 0) mutt_set_charset (Charset); } else { + rfc822_free_address ((ADDRESS **) MuttVars[idx].data); *((ADDRESS **) MuttVars[idx].data) = rfc822_parse_adrlist (NULL, tmp->data); } } diff --git a/main.c b/main.c --- a/main.c +++ b/main.c @@ -435,6 +435,12 @@ static void show_version (void) "+ICONV_NONTRANS " #else "-ICONV_NONTRANS " +#endif + +#ifdef HAVE_ICONV_LIST + "+ICONV_LIST " +#else + "-ICONV_LIST " #endif #if HAVE_LIBIDN