Re: Character set validation (with patch)
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