442c5dc09dda6d466191e42178787a6da27caff2
[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;
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                 charset = g_strndup(eword_begin_p + 2,
91                                     encoding_begin_p - (eword_begin_p + 2));
92                 encoding = toupper(*(encoding_begin_p + 1));
93
94                 if (encoding == 'B') {
95                         gchar *encoded_text;
96
97                         encoded_text =
98                                 g_strndup(text_begin_p + 1,
99                                           eword_end_p - (text_begin_p + 1));
100                         decoded_text = g_malloc
101                                 (eword_end_p - (text_begin_p + 1) + 1);
102
103                         len = from64tobits(decoded_text, encoded_text); //
104                         decoded_text[len] = '\0';
105
106                         g_free(encoded_text);
107                 } else if (encoding == 'Q') {
108                         const gchar *ep = text_begin_p + 1;
109                         gchar *dp;
110
111                         dp = decoded_text = g_malloc(eword_end_p - ep + 1);
112
113                         while (ep < eword_end_p) {
114                                 if (*ep == '=' && ep + 3 <= eword_end_p) {
115                                         if (get_hex_value(dp, ep[1], ep[2])
116                                             == TRUE) {
117                                                 ep += 3;
118                                         } else {
119                                                 *dp = *ep++;
120                                         }
121                                 } else if (*ep == '_') {
122                                         *dp = ' ';
123                                         ep++;
124                                 } else {
125                                         *dp = *ep++;
126                                 }
127                                 dp++;
128                         }
129
130                         *dp = '\0';
131                 } else {
132                         memcpy(outp, p, eword_end_p + 2 - p);
133                         outp += eword_end_p + 2 - p;
134                         p = eword_end_p + 2;
135                         g_free(charset);
136                         continue;
137                 }
138
139                 /* convert to locale encoding */
140                 conv_str = conv_codeset_strdup(decoded_text, charset, NULL);
141                 if (conv_str) {
142                         len = strlen(conv_str);
143                         memcpy(outp, conv_str, len);
144                         g_free(conv_str);
145                 } else {
146                         len = strlen(decoded_text);
147                         memcpy(outp, decoded_text, len);
148                 }
149                 outp += len;
150
151                 g_free(decoded_text);
152                 g_free(charset);
153
154                 p = eword_end_p + 2;
155         }
156
157         *outp = '\0';
158 }
159
160 gint unmime_quoted_printable_line(gchar *str)
161 {
162         gchar *inp = str, *outp = str;
163
164         while (*inp != '\0') {
165                 if (*inp == '=') {
166                         if (inp[1] && inp[2] &&
167                             get_hex_value(outp, inp[1], inp[2]) == TRUE) {
168                                 inp += 3;
169                         } else if (inp[1] == '\0' || isspace(inp[1])) {
170                                 /* soft line break */
171                                 break;
172                         } else {
173                                 /* broken QP string */
174                                 *outp = *inp++;
175                         }
176                 } else {
177                         *outp = *inp++;
178                 }
179                 outp++;
180         }
181
182         *outp = '\0';
183
184         return outp - str;
185 }
186
187 #define HEX_TO_INT(val, hex)                    \
188 {                                               \
189         gchar c = hex;                          \
190                                                 \
191         if ('0' <= c && c <= '9') {             \
192                 val = c - '0';                  \
193         } else if ('a' <= c && c <= 'f') {      \
194                 val = c - 'a' + 10;             \
195         } else if ('A' <= c && c <= 'F') {      \
196                 val = c - 'A' + 10;             \
197         } else {                                \
198                 val = -1;                       \
199         }                                       \
200 }
201
202 static gboolean get_hex_value(gchar *out, gchar c1, gchar c2)
203 {
204         gint hi, lo;
205
206         HEX_TO_INT(hi, c1);
207         HEX_TO_INT(lo, c2);
208
209         if (hi == -1 || lo == -1)
210                 return FALSE;
211
212         *out = (hi << 4) + lo;
213         return TRUE;
214 }