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

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