talons

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

unmime.c (4300B)


      1 /*
      2  * Claws Mail -- a GTK based, lightweight, and fast e-mail client
      3  * Copyright (C) 1999-2012 Hiroyuki Yamamoto 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 
     20 #include <glib.h>
     21 #include <string.h>
     22 #include <ctype.h>
     23 
     24 #include "utils.h"
     25 #include "codeconv.h"
     26 #include "quoted-printable.h"
     27 
     28 #define ENCODED_WORD_BEGIN	"=?"
     29 #define ENCODED_WORD_END	"?="
     30 
     31 /* Decodes headers based on RFC2045 and RFC2047. */
     32 
     33 gchar *unmime_header(const gchar *encoded_str, gboolean addr_field)
     34 {
     35 	const gchar *p = encoded_str;
     36 	const gchar *eword_begin_p, *encoding_begin_p, *text_begin_p,
     37 		    *eword_end_p;
     38 	gchar charset[32];
     39 	gchar encoding;
     40 	gchar *conv_str;
     41 	GString *outbuf;
     42 	gchar *out_str;
     43 	gsize out_len;
     44 	int in_quote = FALSE;
     45 
     46 	cm_return_val_if_fail(encoded_str != NULL, NULL);
     47 
     48 	outbuf = g_string_sized_new(strlen(encoded_str) * 2);
     49 
     50 	while (*p != '\0') {
     51 		gchar *decoded_text = NULL;
     52 		const gchar *quote_p;
     53 		gint len;
     54 
     55 		eword_begin_p = strstr(p, ENCODED_WORD_BEGIN);
     56 		if (!eword_begin_p) {
     57 			g_string_append(outbuf, p);
     58 			break;
     59 		}
     60 
     61 		quote_p = p;
     62 		while ((quote_p = strchr(quote_p, '"')) != NULL) {
     63 			if (quote_p && quote_p < eword_begin_p) {
     64 				/* Found a quote before the encoded word. */
     65 				in_quote = !in_quote;
     66 				quote_p++;
     67 			}
     68 			if (quote_p >= eword_begin_p)
     69 				break;
     70 		}
     71 
     72 		encoding_begin_p = strchr(eword_begin_p + 2, '?');
     73 		if (!encoding_begin_p) {
     74 			g_string_append(outbuf, p);
     75 			break;
     76 		}
     77 		text_begin_p = strchr(encoding_begin_p + 1, '?');
     78 		if (!text_begin_p) {
     79 			g_string_append(outbuf, p);
     80 			break;
     81 		}
     82 		eword_end_p = strstr(text_begin_p + 1, ENCODED_WORD_END);
     83 		if (!eword_end_p) {
     84 			g_string_append(outbuf, p);
     85 			break;
     86 		}
     87 
     88 		if (p == encoded_str) {
     89 			g_string_append_len(outbuf, p, eword_begin_p - p);
     90 			p = eword_begin_p;
     91 		} else {
     92 			/* ignore spaces between encoded words */
     93 			const gchar *sp;
     94 
     95 			for (sp = p; sp < eword_begin_p; sp++) {
     96 				if (!g_ascii_isspace(*sp)) {
     97 					g_string_append_len
     98 						(outbuf, p, eword_begin_p - p);
     99 					p = eword_begin_p;
    100 					break;
    101 				}
    102 			}
    103 		}
    104 
    105 		len = MIN(sizeof(charset) - 1,
    106 			  encoding_begin_p - (eword_begin_p + 2));
    107 		memcpy(charset, eword_begin_p + 2, len);
    108 		charset[len] = '\0';
    109 		encoding = g_ascii_toupper(*(encoding_begin_p + 1));
    110 
    111 		if (encoding == 'B') {
    112 			gchar *tmp;
    113 			tmp = g_strndup(text_begin_p + 1, eword_end_p - (text_begin_p + 1) + 1);
    114 			decoded_text = g_base64_decode(tmp, &out_len);
    115 			g_free(tmp);
    116 		} else if (encoding == 'Q') {
    117 			decoded_text = g_malloc
    118 				(eword_end_p - (text_begin_p + 1) + 1);
    119 			len = qp_decode_q_encoding
    120 				(decoded_text, text_begin_p + 1,
    121 				 eword_end_p - (text_begin_p + 1));
    122 		} else {
    123 			g_string_append_len(outbuf, p, eword_end_p + 2 - p);
    124 			p = eword_end_p + 2;
    125 			continue;
    126 		}
    127 
    128 		/* An encoded word MUST not appear within a quoted string,
    129 		 * so quoting that word after decoding should be safe.
    130 		 * We check there are no quotes just to be sure. If there
    131 		 * are, well, the comma won't pose a problem, probably.
    132 		 */
    133 		if (addr_field && strchr(decoded_text, ',') && !in_quote &&
    134 		    !strchr(decoded_text, '"')) {
    135 			gchar *tmp = g_strdup_printf("\"%s\"", decoded_text);
    136 			g_free(decoded_text);
    137 			decoded_text = tmp;
    138 		}
    139 
    140 		/* convert to UTF-8 */
    141 		conv_str = conv_codeset_strdup(decoded_text, charset, NULL);
    142 		if (!conv_str || !g_utf8_validate(conv_str, -1, NULL)) {
    143 			g_free(conv_str);
    144 			conv_str = g_malloc(len + 1);
    145 			conv_utf8todisp(conv_str, len + 1, decoded_text);
    146 		}
    147 		g_string_append(outbuf, conv_str);
    148 		g_free(conv_str);
    149 
    150 		g_free(decoded_text);
    151 
    152 		p = eword_end_p + 2;
    153 	}
    154 
    155 	out_len = outbuf->len;
    156 	out_str = g_string_free(outbuf, FALSE);
    157 
    158 	return g_realloc(out_str, out_len + 1);
    159 }