fix folder flicker introduced in claws22
[claws.git] / src / unmime.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2002 Hiroyuki Yamamoto
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 2 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, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include <glib.h>
25 #include <string.h>
26 #include <ctype.h>
27
28 #include "codeconv.h"
29 #include "base64.h"
30
31 #define ENCODED_WORD_BEGIN      "=?"
32 #define ENCODED_WORD_END        "?="
33
34 static gboolean get_hex_value(gchar *out, gchar c1, gchar c2);
35
36 /* Decodes headers based on RFC2045 and RFC2047. */
37
38 void unmime_header(gchar *out, const gchar *str)
39 {
40         const gchar *p = str;
41         gchar *outp = out;
42         const gchar *sp;
43         const gchar *eword_begin_p, *encoding_begin_p, *text_begin_p,
44                     *eword_end_p;
45         gchar charset[32];
46         gchar encoding;
47         gchar *conv_str;
48         gint len;
49
50         while (*p != '\0') {
51                 gchar *decoded_text = NULL;
52
53                 eword_begin_p = strstr(p, ENCODED_WORD_BEGIN);
54                 if (!eword_begin_p) {
55                         strcpy(outp, p);
56                         return;
57                 }
58                 encoding_begin_p = strchr(eword_begin_p + 2, '?');
59                 if (!encoding_begin_p) {
60                         strcpy(outp, p);
61                         return;
62                 }
63                 text_begin_p = strchr(encoding_begin_p + 1, '?');
64                 if (!text_begin_p) {
65                         strcpy(outp, p);
66                         return;
67                 }
68                 eword_end_p = strstr(text_begin_p + 1, ENCODED_WORD_END);
69                 if (!eword_end_p) {
70                         strcpy(outp, p);
71                         return;
72                 }
73
74                 if (p == str) {
75                         memcpy(outp, p, eword_begin_p - p);
76                         outp += eword_begin_p - p;
77                         p = eword_begin_p;
78                 } else {
79                         /* ignore spaces between encoded words */
80                         for (sp = p; sp < eword_begin_p; sp++) {
81                                 if (!isspace(*sp)) {
82                                         memcpy(outp, p, eword_begin_p - p);
83                                         outp += eword_begin_p - p;
84                                         p = eword_begin_p;
85                                         break;
86                                 }
87                         }
88                 }
89
90                 len = MIN(sizeof(charset) - 1,
91                           encoding_begin_p - (eword_begin_p + 2));
92                 memcpy(charset, eword_begin_p + 2, len);
93                 charset[len] = '\0';
94                 encoding = toupper(*(encoding_begin_p + 1));
95
96                 if (encoding == 'B') {
97                         decoded_text = g_malloc
98                                 (eword_end_p - (text_begin_p + 1) + 1);
99                         len = base64_decode(decoded_text, text_begin_p + 1,
100                                             eword_end_p - (text_begin_p + 1));
101                         decoded_text[len] = '\0';
102                 } else if (encoding == 'Q') {
103                         const gchar *ep = text_begin_p + 1;
104                         gchar *dp;
105
106                         dp = decoded_text = g_malloc(eword_end_p - ep + 1);
107
108                         while (ep < eword_end_p) {
109                                 if (*ep == '=' && ep + 3 <= eword_end_p) {
110                                         if (get_hex_value(dp, ep[1], ep[2])
111                                             == TRUE) {
112                                                 ep += 3;
113                                         } else {
114                                                 *dp = *ep++;
115                                         }
116                                 } else if (*ep == '_') {
117                                         *dp = ' ';
118                                         ep++;
119                                 } else {
120                                         *dp = *ep++;
121                                 }
122                                 dp++;
123                         }
124
125                         *dp = '\0';
126                 } else {
127                         memcpy(outp, p, eword_end_p + 2 - p);
128                         outp += eword_end_p + 2 - p;
129                         p = eword_end_p + 2;
130                         continue;
131                 }
132
133                 /* convert to locale encoding */
134                 conv_str = conv_codeset_strdup(decoded_text, charset, NULL);
135                 if (conv_str) {
136                         len = strlen(conv_str);
137                         memcpy(outp, conv_str, len);
138                         g_free(conv_str);
139                 } else {
140                         len = strlen(decoded_text);
141                         memcpy(outp, decoded_text, len);
142                 }
143                 outp += len;
144
145                 g_free(decoded_text);
146
147                 p = eword_end_p + 2;
148         }
149
150         *outp = '\0';
151 }
152
153 gint unmime_quoted_printable_line(gchar *str)
154 {
155         gchar *inp = str, *outp = str;
156
157         while (*inp != '\0') {
158                 if (*inp == '=') {
159                         if (inp[1] && inp[2] &&
160                             get_hex_value(outp, inp[1], inp[2]) == TRUE) {
161                                 inp += 3;
162                         } else if (inp[1] == '\0' || isspace(inp[1])) {
163                                 /* soft line break */
164                                 break;
165                         } else {
166                                 /* broken QP string */
167                                 *outp = *inp++;
168                         }
169                 } else {
170                         *outp = *inp++;
171                 }
172                 outp++;
173         }
174
175         *outp = '\0';
176
177         return outp - str;
178 }
179
180 #define HEX_TO_INT(val, hex)                    \
181 {                                               \
182         gchar c = hex;                          \
183                                                 \
184         if ('0' <= c && c <= '9') {             \
185                 val = c - '0';                  \
186         } else if ('a' <= c && c <= 'f') {      \
187                 val = c - 'a' + 10;             \
188         } else if ('A' <= c && c <= 'F') {      \
189                 val = c - 'A' + 10;             \
190         } else {                                \
191                 val = -1;                       \
192         }                                       \
193 }
194
195 static gboolean get_hex_value(gchar *out, gchar c1, gchar c2)
196 {
197         gint hi, lo;
198
199         HEX_TO_INT(hi, c1);
200         HEX_TO_INT(lo, c2);
201
202         if (hi == -1 || lo == -1)
203                 return FALSE;
204
205         *out = (hi << 4) + lo;
206         return TRUE;
207 }