talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

entity.c (7959B)


      1 /*
      2  * Claws Mail -- a GTK based, lightweight, and fast e-mail client
      3  * Copyright (C) 2017 Ricardo Mones and the Claws Mail team
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License as published by
      7  * the Free Software Foundation; either version 3 of the License, or
      8  * (at your option) any later version.
      9  *
     10  * This program is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  * GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License
     16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
     17  */
     18 
     19 #include "utils.h"
     20 
     21 #define ENTITY_MAX_LEN 8
     22 #define DECODED_MAX_LEN 6
     23 
     24 static GHashTable *symbol_table = NULL;
     25 
     26 typedef struct _EntitySymbol EntitySymbol;
     27 
     28 struct _EntitySymbol
     29 {
     30 	gchar *const key;
     31 	gchar *const value;
     32 };
     33 
     34 /* in alphabetical order with upper-case version first */
     35 static EntitySymbol symbolic_entities[] = {
     36 	/* A */
     37 	{"Aacute", "Á"},
     38 	{"aacute", "á"},
     39 	{"Acirc", "Â"},
     40 	{"acirc", "â"},
     41 	{"acute", "´"},
     42 	{"AElig", "Æ"},
     43 	{"aelig", "æ"},
     44 	{"Agrave", "À"},
     45 	{"agrave", "à"},
     46 	{"alefsym", "ℵ"},
     47 	{"Alpha", "Α"},
     48 	{"alpha", "α"},
     49 	{"amp", "&"},
     50 	{"and", "∧"},
     51 	{"ang", "∠"},
     52 	{"apos", "'"},
     53 	{"Aring", "Å"},
     54 	{"aring", "å"},
     55 	{"asymp", "≈"},
     56 	{"Atilde", "Ã"},
     57 	{"atilde", "ã"},
     58 	{"Auml", "Ä"},
     59 	{"auml", "ä"},
     60 	/* B */
     61 	{"bdquo", "„"},
     62 	{"Beta", "Β"},
     63 	{"beta", "β"},
     64 	{"brvbar", "¦"},
     65 	{"bull", "•"},
     66 	/* C */
     67 	{"cap", "∩"},
     68 	{"Ccedil", "Ç"},
     69 	{"ccedil", "ç"},
     70 	{"cedil", "¸"},
     71 	{"cent", "¢"},
     72 	{"Chi", "Χ"},
     73 	{"chi", "χ"},
     74 	{"circ", "ˆ"},
     75 	{"clubs", "♣"},
     76 	{"cong", "≅"},
     77 	{"copy", "©"},
     78 	{"crarr", "↵"},
     79 	{"cup", "∪"},
     80 	{"curren", "¤"},
     81 	/* D */
     82 	{"dagger", "†"},
     83 	{"Dagger", "‡"},
     84 	{"dArr", "⇓"},
     85 	{"darr", "↓"},
     86 	{"deg", "°"},
     87 	{"Delta", "Δ"},
     88 	{"delta", "δ"},
     89 	{"diams", "♦"},
     90 	{"divide", "÷"},
     91 	/* E */
     92 	{"Eacute", "É"},
     93 	{"eacute", "é"},
     94 	{"Ecirc", "Ê"},
     95 	{"ecirc", "ê"},
     96 	{"Egrave", "È"},
     97 	{"egrave", "è"},
     98 	{"empty", "∅"},
     99 	{"emsp", "\xE2\x80\x83"},
    100 	{"ensp", "\xE2\x80\x82"},
    101 	{"Epsilon", "Ε"},
    102 	{"epsilon", "ε"},
    103 	{"equiv", "≡"},
    104 	{"Eta", "Η"},
    105 	{"eta", "η"},
    106 	{"ETH", "Ð"},
    107 	{"eth", "ð"},
    108 	{"Euml", "Ë"},
    109 	{"euml", "ë"},
    110 	{"euro", "€"},
    111 	{"exist", "∃"},
    112 	/* F */
    113 	{"fnof", "ƒ"},
    114 	{"forall", "∀"},
    115 	{"frac12", "½"},
    116 	{"frac14", "¼"},
    117 	{"frac34", "¾"},
    118 	{"frasl", "⁄"},
    119 	/* G */
    120 	{"Gamma", "Γ"},
    121 	{"gamma", "γ"},
    122 	{"ge", "≥"},
    123 	{"gt", ">"},
    124 	/* H */
    125 	{"hArr", "⇔"},
    126 	{"harr", "↔"},
    127 	{"hearts", "♥"},
    128 	{"hellip", "…"},
    129 	/* I */
    130 	{"Iacute", "Í"},
    131 	{"iacute", "í"},
    132 	{"IArr", "⇐"},
    133 	{"Icirc", "Î"},
    134 	{"icirc", "î"},
    135 	{"iexcl", "¡"},
    136 	{"Igrave", "Ì"},
    137 	{"igrave", "ì"},
    138 	{"image", "ℑ"},
    139 	{"infin", "∞"},
    140 	{"int", "∫"},
    141 	{"Iota", "Ι"},
    142 	{"iota", "ι"},
    143 	{"iquest", "¿"},
    144 	{"isin", "∈"},
    145 	{"Iuml", "Ï"},
    146 	{"iuml", "ï"},
    147 	/* K */
    148 	{"Kappa", "Κ"},
    149 	{"kappa", "κ"},
    150 	/* L */
    151 	{"Lambda", "Λ"},
    152 	{"lambda", "λ"},
    153 	{"lang", "〈"},
    154 	{"laquo", "«"},
    155 	{"larr", "←"},
    156 	{"lceil", "⌈"},
    157 	{"ldquo", "“"},
    158 	{"le", "≤"},
    159 	{"lfloor", "⌊"},
    160 	{"lowast", "∗"},
    161 	{"loz", "◊"},
    162 	{"lrm", "\xE2\x80\x8E"},
    163 	{"lsaquo", "‹"},
    164 	{"lsquo", "‘"},
    165 	{"lt", "<"},
    166 	/* M */
    167 	{"macr", "¯"},
    168 	{"mdash", "—"},
    169 	{"micro", "µ"},
    170 	{"middot", "·"},
    171 	{"minus", "−"},
    172 	{"Mu", "Μ"},
    173 	{"mu", "μ"},
    174 	/* N */
    175 	{"nabla", "∇"},
    176 	{"nbsp", "\xC2\xA0"},
    177 	{"ndash", "–"},
    178 	{"ne", "≠"},
    179 	{"ni", "∋"},
    180 	{"not", "¬"},
    181 	{"notin", "∉"},
    182 	{"nsub", "⊄"},
    183 	{"Ntilde", "Ñ"},
    184 	{"ntilde", "ñ"},
    185 	{"Nu", "Ν"},
    186 	{"nu", "ν"},
    187 	/* O */
    188 	{"Oacute", "Ó"},
    189 	{"oacute", "ó"},
    190 	{"Ocirc", "Ô"},
    191 	{"ocirc", "ô"},
    192 	{"OElig", "Œ"},
    193 	{"oelig", "œ"},
    194 	{"Ograve", "Ò"},
    195 	{"ograve", "ò"},
    196 	{"oline", "‾"},
    197 	{"Omega", "Ω"},
    198 	{"omega", "ω"},
    199 	{"Omicron", "Ο"},
    200 	{"omicron", "ο"},
    201 	{"oplus", "⊕"},
    202 	{"or", "∨"},
    203 	{"ordf", "ª"},
    204 	{"ordm", "º"},
    205 	{"Oslash", "Ø"},
    206 	{"oslash", "ø"},
    207 	{"Otilde", "Õ"},
    208 	{"otilde", "õ"},
    209 	{"otimes", "⊗"},
    210 	{"Ouml", "Ö"},
    211 	{"ouml", "ö"},
    212 	/* P */
    213 	{"para", "¶"},
    214 	{"part", "∂"},
    215 	{"permil", "‰"},
    216 	{"perp", "⊥"},
    217 	{"Phi", "Φ"},
    218 	{"phi", "φ"},
    219 	{"Pi", "Π"},
    220 	{"pi", "π"},
    221 	{"piv", "ϖ"},
    222 	{"plusmn", "±"},
    223 	{"pound", "£"},
    224 	{"Prime", "″"},
    225 	{"prime", "′"},
    226 	{"prod", "∏"},
    227 	{"prop", "∝"},
    228 	{"Psi", "Ψ"},
    229 	{"psi", "ψ"},
    230 	/* Q */
    231 	{"quot", "\""},
    232 	/* R */
    233 	{"radic", "√"},
    234 	{"rang", "〉"},
    235 	{"raquo", "»"},
    236 	{"rArr", "⇒"},
    237 	{"rarr", "→"},
    238 	{"rceil", "⌉"},
    239 	{"rdquo", "”"},
    240 	{"real", "ℜ"},
    241 	{"reg", "®"},
    242 	{"rfloor", "⌋"},
    243 	{"Rho", "Ρ"},
    244 	{"rho", "ρ"},
    245 	{"rlm", "\xE2\x80\x8F"},
    246 	{"rsaquo", "›"},
    247 	{"rsquo", "’"},
    248 	/* S */
    249 	{"sbquo", "‚"},
    250 	{"Scaron", "Š"},
    251 	{"scaron", "š"},
    252 	{"sdot", "⋅"},
    253 	{"sect", "§"},
    254 	{"shy", "\xC2\xAD"},
    255 	{"Sigma", "Σ"},
    256 	{"sigma", "σ"},
    257 	{"sigmaf", "ς"},
    258 	{"sim", "∼"},
    259 	{"spades", "♠"},
    260 	{"sub", "⊂"},
    261 	{"sube", "⊆"},
    262 	{"sum", "∑"},
    263 	{"sup", "⊃"},
    264 	{"sup1", "¹"},
    265 	{"sup2", "²"},
    266 	{"sup3", "³"},
    267 	{"supe", "⊇"},
    268 	{"szlig", "ß"},
    269 	/* T */
    270 	{"Tau", "Τ"},
    271 	{"tau", "τ"},
    272 	{"there4", "∴"},
    273 	{"Theta", "Θ"},
    274 	{"theta", "θ"},
    275 	{"thetasym", "ϑ"},
    276 	{"thinsp", "\xE2\x80\x89"},
    277 	{"THORN", "Þ"},
    278 	{"thorn", "þ"},
    279 	{"tilde", "˜"},
    280 	{"times", "×"},
    281 	{"trade", "™"},
    282 	/* U */
    283 	{"Uacute", "Ú"},
    284 	{"uacute", "ú"},
    285 	{"uArr", "⇑"},
    286 	{"uarr", "↑"},
    287 	{"Ucirc", "Û"},
    288 	{"ucirc", "û"},
    289 	{"Ugrave", "Ù"},
    290 	{"ugrave", "ù"},
    291 	{"uml", "¨"},
    292 	{"upsih", "ϒ"},
    293 	{"Upsilon", "Υ"},
    294 	{"upsilon", "υ"},
    295 	{"Uuml", "Ü"},
    296 	{"uuml", "ü"},
    297 	/* W */
    298 	{"weierp", "℘"},
    299 	/* X */
    300 	{"Xi", "Ξ"},
    301 	{"xi", "ξ"},
    302 	/* Y */
    303 	{"Yacute", "Ý"},
    304 	{"yacute", "ý"},
    305 	{"yen", "¥"},
    306 	{"Yuml", "Ÿ"},
    307 	{"yuml", "ÿ"},
    308 	/* Z */
    309 	{"Zeta", "Ζ"},
    310 	{"zeta", "ζ"},
    311 	{"zwj", "\xE2\x80\x8D"},
    312 	{"zwnj", "\xE2\x80\x8C"},
    313 	{NULL, NULL}
    314 };
    315 
    316 static gchar* entity_extract_to_buffer(gchar *p, gchar b[])
    317 {
    318 	gint i = 0;
    319 
    320 	while (*p != '\0' && *p != ';' && i < ENTITY_MAX_LEN) {
    321 		b[i] = *p;
    322 		++i, ++p;
    323 	}
    324 	if (*p != ';' || i == 0 || i == ENTITY_MAX_LEN)
    325 		return NULL;
    326 	b[i] = '\0';
    327 
    328 	return b;
    329 }
    330 
    331 static gchar *entity_decode_numeric(gchar *str)
    332 {
    333 	gchar b[ENTITY_MAX_LEN];
    334 	gchar *p = str, *res;
    335 	gboolean hex = FALSE;
    336 	gunichar c = 0;
    337 	gint ret;
    338 
    339 	++p;
    340 	if (*p == '\0')
    341 		return NULL;
    342 
    343 	if (*p == 'x') {
    344 		hex = TRUE;
    345 		++p;
    346 		if (*p == '\0')
    347 			return NULL;
    348 	}
    349 
    350 	if (entity_extract_to_buffer (p, b) == NULL)
    351 		return NULL;
    352 
    353 	if (strlen(b) > 0)
    354 		c = g_ascii_strtoll (b, NULL, (hex ? 16 : 10));
    355 
    356 	if (c < 32)
    357 		/* An unprintable character; return the Unicode replacement symbol */
    358 		return g_strdup("\xef\xbf\xbd");
    359 
    360 	if (!g_unichar_validate(c)) {
    361 		/* Make sure the character is valid Unicode */
    362 		debug_print("Numeric reference '&#%s;' is invalid in Unicode codespace\n", b);
    363 		return NULL;
    364 	}
    365 
    366 	res = g_malloc0 (DECODED_MAX_LEN + 1);
    367 	ret = g_unichar_to_utf8 (c, res);
    368 	if (ret == 0) {
    369 		debug_print("Failed to convert unicode character %u to UTF-8\n", c);
    370 		g_free(res);
    371 		res = NULL;
    372 	}
    373 
    374 	return res;
    375 }
    376 
    377 static gchar *entity_decode_symbol(gchar *str)
    378 {
    379 	gchar b[ENTITY_MAX_LEN];
    380 	gchar *decoded;
    381 
    382 	if (entity_extract_to_buffer (str, b) == NULL)
    383 		return NULL;
    384 
    385 	if (symbol_table == NULL) {
    386 		gint i;
    387 
    388 		symbol_table = g_hash_table_new (g_str_hash, g_str_equal);
    389 		for (i = 0; symbolic_entities[i].key != NULL; ++i) {
    390 			g_hash_table_insert (symbol_table,
    391 				symbolic_entities[i].key, symbolic_entities[i].value);
    392 		}
    393 		debug_print("initialized entities table with %d symbols\n", i);
    394 	}
    395 
    396 	decoded = g_hash_table_lookup (symbol_table, b);
    397 	if (decoded != NULL)
    398 		return g_strdup (decoded);
    399 
    400 	return NULL;
    401 }
    402 
    403 gchar *entity_decode(gchar *str)
    404 {
    405 	gchar *p = str;
    406 	if (p == NULL || *p != '&')
    407 		return NULL;
    408 	++p;
    409 	if (*p == '\0')
    410 		return NULL;
    411 	if (*p == '#')
    412 		return entity_decode_numeric(p);
    413 	else
    414 		return entity_decode_symbol(p);
    415 }