fix bug 3561, 'HTML <a> tag with no href makes message display incorrectly.'
[claws.git] / src / html.c
index 46f741480f7af5f90b9f0fd0c72ffd3a2b94cf46..72df9464449d52e5b86311c9577f96a66d6d4066 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail team
+ * Copyright (C) 1999-2012 Hiroyuki Yamamoto and the Claws Mail team
  *
  * 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
@@ -331,8 +331,8 @@ SC_HTMLParser *sc_html_parser_new(FILE *fp, CodeConverter *conv)
 {
        SC_HTMLParser *parser;
 
-       g_return_val_if_fail(fp != NULL, NULL);
-       g_return_val_if_fail(conv != NULL, NULL);
+       cm_return_val_if_fail(fp != NULL, NULL);
+       cm_return_val_if_fail(conv != NULL, NULL);
 
        parser = g_new0(SC_HTMLParser, 1);
        parser->fp = fp;
@@ -440,11 +440,17 @@ static SC_HTMLState sc_html_read_line(SC_HTMLParser *parser)
        gchar buf[SC_HTMLBUFSIZE];
        gchar buf2[SC_HTMLBUFSIZE];
        gint index;
+       gint n;
+
+       if (parser->fp == NULL)
+               return SC_HTML_EOF;
 
-       if (fgets(buf, sizeof(buf), parser->fp) == NULL) {
+       n = fread(buf, 1, sizeof(buf) - 1, parser->fp);
+       if (n == 0) {
                parser->state = SC_HTML_EOF;
                return SC_HTML_EOF;
-       }
+       } else
+               buf[n] = '\0';
 
        if (conv_convert(parser->conv, buf2, sizeof(buf2), buf) < 0) {
                index = parser->bufp - parser->buf->str;
@@ -519,7 +525,7 @@ static SC_HTMLTag *sc_html_get_tag(const gchar *str)
        gchar *tmp;
        guchar *tmpp;
 
-       g_return_val_if_fail(str != NULL, NULL);
+       cm_return_val_if_fail(str != NULL, NULL);
 
        if (*str == '\0' || *str == '!') return NULL;
 
@@ -531,13 +537,11 @@ static SC_HTMLTag *sc_html_get_tag(const gchar *str)
                ;
 
        if (*tmpp == '\0') {
-               g_strdown(tmp);
-               tag->name = g_strdup(tmp);
+               tag->name = g_utf8_strdown(tmp, -1);
                return tag;
        } else {
                *tmpp++ = '\0';
-               g_strdown(tmp);
-               tag->name = g_strdup(tmp);
+               tag->name = g_utf8_strdown(tmp, -1);
        }
 
        while (*tmpp != '\0') {
@@ -568,7 +572,7 @@ static SC_HTMLTag *sc_html_get_tag(const gchar *str)
                                tmpp++;
                                attr_value = tmpp;
                                if ((p = strchr(attr_value, quote)) == NULL) {
-                                       g_warning("sc_html_get_tag(): syntax error in tag: '%s'\n", str);
+                                       g_warning("sc_html_get_tag(): syntax error in tag: '%s'", str);
                                        return tag;
                                }
                                tmpp = p;
@@ -585,9 +589,8 @@ static SC_HTMLTag *sc_html_get_tag(const gchar *str)
                        attr_value = "";
 
                g_strchomp(attr_name);
-               g_strdown(attr_name);
                attr = g_new(SC_HTMLAttr, 1);
-               attr->name = g_strdup(attr_name);
+               attr->name = g_utf8_strdown(attr_name, -1);
                attr->value = g_strdup(attr_value);
                tag->attr = g_list_append(tag->attr, attr);
        }
@@ -610,6 +613,24 @@ static void sc_html_free_tag(SC_HTMLTag *tag)
        g_free(tag);
 }
 
+static void decode_href(SC_HTMLParser *parser)
+{
+       gchar *tmp;
+       SC_HTMLParser *tparser = g_new0(SC_HTMLParser, 1);
+
+       tparser->str = g_string_new(NULL);
+       tparser->buf = g_string_new(parser->href);
+       tparser->bufp = tparser->buf->str;
+       tparser->symbol_table = default_symbol_table;
+       
+       tmp = sc_html_parse(tparser);
+       
+       g_free(parser->href);
+       parser->href = g_strdup(tmp);
+
+       sc_html_parser_destroy(tparser);
+}
+
 static SC_HTMLState sc_html_parse_tag(SC_HTMLParser *parser)
 {
        gchar buf[SC_HTMLBUFSIZE];
@@ -622,20 +643,25 @@ static SC_HTMLState sc_html_parse_tag(SC_HTMLParser *parser)
        parser->state = SC_HTML_UNKNOWN;
        if (!tag) return SC_HTML_UNKNOWN;
 
-       if (!strcmp(tag->name, "br")) {
+       if (!strcmp(tag->name, "br") || !strcmp(tag->name, "br/")) {
                parser->space = FALSE;
                sc_html_append_char(parser, '\n');
                parser->state = SC_HTML_BR;
        } else if (!strcmp(tag->name, "a")) {
                GList *cur;
+               parser->href = NULL;
                for (cur = tag->attr; cur != NULL; cur = cur->next) {
                        if (cur->data && !strcmp(((SC_HTMLAttr *)cur->data)->name, "href")) {
                                g_free(parser->href);
                                parser->href = g_strdup(((SC_HTMLAttr *)cur->data)->value);
+                               decode_href(parser);
                                parser->state = SC_HTML_HREF_BEG;
                                break;
                        }
                }
+               if (parser->href == NULL)
+                       parser->href = g_strdup("");
+               parser->state = SC_HTML_HREF_BEG;
        } else if (!strcmp(tag->name, "/a")) {
                parser->state = SC_HTML_HREF;
        } else if (!strcmp(tag->name, "p")) {
@@ -702,7 +728,7 @@ static void sc_html_parse_special(SC_HTMLParser *parser)
        const gchar *val;
 
        parser->state = SC_HTML_UNKNOWN;
-       g_return_if_fail(*parser->bufp == '&');
+       cm_return_if_fail(*parser->bufp == '&');
 
        /* &foo; */
        for (n = 0; parser->bufp[n] != '\0' && parser->bufp[n] != ';'; n++)
@@ -726,12 +752,28 @@ static void sc_html_parse_special(SC_HTMLParser *parser)
        sc_html_append_str(parser, symbol_name, -1);
 }
 
+static gchar *sc_html_find_tag(SC_HTMLParser *parser, const gchar *tag)
+{
+       gchar *cur = parser->bufp;
+       gint len = strlen(tag);
+
+       if (cur == NULL)
+               return NULL;
+
+       while ((cur = strstr(cur, "<")) != NULL) {
+               if (!g_ascii_strncasecmp(cur, tag, len))
+                       return cur;
+               cur += 2;
+       }
+       return NULL;
+}
+
 static void sc_html_get_parenthesis(SC_HTMLParser *parser, gchar *buf, gint len)
 {
        gchar *p;
 
        buf[0] = '\0';
-       g_return_if_fail(*parser->bufp == '<');
+       cm_return_if_fail(*parser->bufp == '<');
 
        /* ignore comment / CSS / script stuff */
        if (!strncmp(parser->bufp, "<!--", 4)) {
@@ -743,14 +785,14 @@ static void sc_html_get_parenthesis(SC_HTMLParser *parser, gchar *buf, gint len)
        }
        if (!g_ascii_strncasecmp(parser->bufp, "<style", 6)) {
                parser->bufp += 6;
-               while ((p = strcasestr(parser->bufp, "</style>")) == NULL)
+               while ((p = sc_html_find_tag(parser, "</style>")) == NULL)
                        if (sc_html_read_line(parser) == SC_HTML_EOF) return;
                parser->bufp = p + 8;
                return;
        }
        if (!g_ascii_strncasecmp(parser->bufp, "<script", 7)) {
                parser->bufp += 7;
-               while ((p = strcasestr(parser->bufp, "</script>")) == NULL)
+               while ((p = sc_html_find_tag(parser, "</script>")) == NULL)
                        if (sc_html_read_line(parser) == SC_HTML_EOF) return;
                parser->bufp = p + 9;
                return;