Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 402 lines (357 sloc) 7.954 kB
e577381 Initial revision
Thomas Roessler authored
1 /*
2 * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 #include "mutt.h"
20 #include "mime.h"
a6d5e5a [patch-0.93.2i.ru.mmultiple_charsets.gz] This patch adds
Thomas Roessler authored
21 #include "charset.h"
e577381 Initial revision
Thomas Roessler authored
22 #include "rfc2047.h"
23
24 #include <ctype.h>
25 #include <string.h>
26
27 typedef void encode_t (char *, size_t, const unsigned char *);
28
29 extern char MimeSpecials[];
30 extern char B64Chars[];
31
32 static void q_encode_string (char *d, size_t dlen, const unsigned char *s)
33 {
34 char charset[SHORT_STRING];
35 size_t cslen, wordlen;
36 char *wptr = d;
37
38 snprintf (charset, sizeof (charset), "=?%s?Q?",
d63919f CVS branch clean-up.
Thomas Roessler authored
39 strcasecmp ("us-ascii", NONULL(Charset)) == 0 ? "unknown-8bit" : NONULL(Charset));
e577381 Initial revision
Thomas Roessler authored
40 cslen = strlen (charset);
41
42 strcpy (wptr, charset);
43 wptr += cslen;
44 wordlen = cslen;
45 dlen -= cslen;
46
47 dlen -= 3; /* save room for the word terminator */
48
49 while (*s && dlen > 0)
50 {
51 if (wordlen >= 72)
52 {
53 if (dlen < 4 + cslen)
54 break;
55
56 strcpy (wptr, "?=\n ");
57 wptr += 4;
58 dlen -= 4;
59 strcpy (wptr, charset);
60 wptr += cslen;
61 wordlen = cslen;
62 dlen -= cslen;
63 }
64
65 if (*s == ' ')
66 {
67 *wptr++ = '_';
68 wordlen++;
69 dlen--;
70 }
71 else if ((*s & 0x80) || *s == '\t' || strchr (MimeSpecials, *s))
72 {
73 if (wordlen >= 70)
74 {
75 if (dlen < 4 + cslen)
76 break;
77
78 strcpy (wptr, "?=\n ");
79 wptr += 4;
80 dlen -= 4;
81
82 strcpy (wptr, charset);
83 wptr += cslen;
84 wordlen = cslen;
85 dlen -= cslen;
86 }
87
88 if (dlen < 3)
89 break;
90 sprintf (wptr, "=%02X", *s);
91 wptr += 3;
92 wordlen += 3;
93 dlen -= 3;
94 }
95 else
96 {
97 *wptr++ = *s;
98 wordlen++;
99 dlen--;
100 }
101 s++;
102 }
103
104 strcpy (wptr, "?=");
105 }
106
107 static void b_encode_string (char *d, size_t dlen, const unsigned char *s)
108 {
109 char charset[SHORT_STRING];
110 char *wptr = d;
111 int cslen;
112 int wordlen;
113
d63919f CVS branch clean-up.
Thomas Roessler authored
114 snprintf (charset, sizeof (charset), "=?%s?B?", NONULL(Charset));
e577381 Initial revision
Thomas Roessler authored
115 cslen = strlen (charset);
116 strcpy (wptr, charset);
117 wptr += cslen;
118 wordlen = cslen;
119 dlen -= cslen;
120
121 dlen -= 3; /* save room for the word terminator */
122
123 while (*s && dlen >= 4)
124 {
125 if (wordlen >= 71)
126 {
127 if (dlen < 4 + cslen)
128 break;
129
130 strcpy (wptr, "?=\n ");
131 wptr += 4;
132 dlen -= 4;
133
134 strcpy (wptr, charset);
135 wptr += cslen;
136 wordlen = cslen;
137 dlen -= cslen;
138 }
139
140 *wptr++ = B64Chars[ (*s >> 2) & 0x3f ];
141 *wptr++ = B64Chars[ ((*s & 0x3) << 4) | ((*(s+1) >> 4) & 0xf) ];
142 s++;
143 if (*s)
144 {
145 *wptr++ = B64Chars[ ((*s & 0xf) << 2) | ((*(s+1) >> 6) & 0x3) ];
146 s++;
147 if (*s)
148 {
149 *wptr++ = B64Chars[ *s & 0x3f ];
150 s++;
151 }
152 else
153 *wptr++ = '=';
154 }
155 else
156 {
157 *wptr++ = '=';
158 *wptr++ = '=';
159 }
160
161 wordlen += 4;
162 dlen -= 4;
163 }
164
165 strcpy (wptr, "?=");
166 }
167
168 void rfc2047_encode_string (char *d, size_t dlen, const unsigned char *s)
169 {
170 int count = 0;
171 int len;
172 const unsigned char *p = s;
173 encode_t *encoder;
174
175 /* First check to see if there are any 8-bit characters */
176 for (; *p; p++)
177 {
178 if (*p & 0x80)
179 count++;
d63919f CVS branch clean-up.
Thomas Roessler authored
180 else if (*p == '=' && *(p+1) == '?')
e577381 Initial revision
Thomas Roessler authored
181 {
182 count += 2;
183 p++;
184 }
185 }
186 if (!count)
187 {
188 strfcpy (d, (const char *)s, dlen);
189 return;
190 }
191
d63919f CVS branch clean-up.
Thomas Roessler authored
192 if (strcasecmp("us-ascii", NONULL(Charset)) == 0 ||
193 strncasecmp("iso-8859", NONULL(Charset), 8) == 0)
e577381 Initial revision
Thomas Roessler authored
194 encoder = q_encode_string;
195 else
196 {
197 /* figure out which encoding generates the most compact representation */
198 len = strlen ((char *) s);
199 if ((count * 2) + len <= (4 * len) / 3)
200 encoder = q_encode_string;
201 else
202 encoder = b_encode_string;
203 }
204
205 /* Hack to pull the Re: and Fwd: out of the encoded word for better
206 handling by agents which do not support RFC2047. */
207 if (!strncasecmp ("re: ", (char *) s, 4))
208 {
209 strncpy (d, (char *) s, 4);
210 d += 4;
211 dlen -= 4;
212 s += 4;
213 }
214 else if (!strncasecmp ("fwd: ", (char *) s, 5))
215 {
216 strncpy (d, (char *) s, 5);
217 d += 5;
218 dlen -= 5;
219 s += 5;
220 }
221
222 (*encoder) (d, dlen, s);
223 }
224
225 void rfc2047_encode_adrlist (ADDRESS *addr)
226 {
227 ADDRESS *ptr = addr;
228 char buffer[STRING];
229
230 while (ptr)
231 {
232 if (ptr->personal)
233 {
234 rfc2047_encode_string (buffer, sizeof (buffer), (const unsigned char *)ptr->personal);
235 safe_free ((void **) &ptr->personal);
236 ptr->personal = safe_strdup (buffer);
237 }
238 ptr = ptr->next;
239 }
240 }
241
242 static int rfc2047_decode_word (char *d, const char *s, size_t len)
243 {
244 char *p = safe_strdup (s);
245 char *pp = p;
246 char *pd = d;
247 int enc = 0, filter = 0, count = 0, c1, c2, c3, c4;
09455c6 This patch adds on-demand loading of character set tables,
Thomas Roessler authored
248 char *charset = NULL;
249
e577381 Initial revision
Thomas Roessler authored
250 while ((pp = strtok (pp, "?")) != NULL)
251 {
252 count++;
253 switch (count)
254 {
255 case 2:
d63919f CVS branch clean-up.
Thomas Roessler authored
256 if (strcasecmp (pp, NONULL(Charset)) != 0)
a6d5e5a [patch-0.93.2i.ru.mmultiple_charsets.gz] This patch adds
Thomas Roessler authored
257 {
e577381 Initial revision
Thomas Roessler authored
258 filter = 1;
a6d5e5a [patch-0.93.2i.ru.mmultiple_charsets.gz] This patch adds
Thomas Roessler authored
259 charset = pp;
260 }
e577381 Initial revision
Thomas Roessler authored
261 break;
262 case 3:
263 if (toupper (*pp) == 'Q')
264 enc = ENCQUOTEDPRINTABLE;
265 else if (toupper (*pp) == 'B')
266 enc = ENCBASE64;
267 else
268 return (-1);
269 break;
270 case 4:
271 if (enc == ENCQUOTEDPRINTABLE)
272 {
273 while (*pp && len > 0)
274 {
275 if (*pp == '_')
276 {
277 *pd++ = ' ';
278 len--;
279 }
280 else if (*pp == '=')
281 {
282 *pd++ = (hexval(pp[1]) << 4) | hexval(pp[2]);
283 len--;
284 pp += 2;
285 }
286 else
287 {
288 *pd++ = *pp;
289 len--;
290 }
291 pp++;
292 }
293 *pd = 0;
294 }
295 else if (enc == ENCBASE64)
296 {
297 while (*pp && len > 0)
298 {
d63919f CVS branch clean-up.
Thomas Roessler authored
299 c1 = base64val(pp[0]);
300 c2 = base64val(pp[1]);
e577381 Initial revision
Thomas Roessler authored
301 *pd++ = (c1 << 2) | ((c2 >> 4) & 0x3);
302 if (--len == 0) break;
303
304 if (pp[2] == '=') break;
305
d63919f CVS branch clean-up.
Thomas Roessler authored
306 c3 = base64val(pp[2]);
e577381 Initial revision
Thomas Roessler authored
307 *pd++ = ((c2 & 0xf) << 4) | ((c3 >> 2) & 0xf);
308 if (--len == 0)
309 break;
310
311 if (pp[3] == '=')
312 break;
313
d63919f CVS branch clean-up.
Thomas Roessler authored
314 c4 = base64val(pp[3]);
e577381 Initial revision
Thomas Roessler authored
315 *pd++ = ((c3 & 0x3) << 6) | c4;
316 if (--len == 0)
317 break;
318
319 pp += 4;
320 }
321 *pd = 0;
322 }
323 break;
324 }
325 pp = 0;
326 }
327 if (filter)
328 {
09455c6 This patch adds on-demand loading of character set tables,
Thomas Roessler authored
329 if (mutt_display_string(d, mutt_get_translation(charset, Charset)) == -1)
e577381 Initial revision
Thomas Roessler authored
330 {
a6d5e5a [patch-0.93.2i.ru.mmultiple_charsets.gz] This patch adds
Thomas Roessler authored
331 pd = d;
332 while (*pd)
333 {
334 if (!IsPrint (*pd))
335 *pd = '?';
336 pd++;
337 }
e577381 Initial revision
Thomas Roessler authored
338 }
339 }
a6d5e5a [patch-0.93.2i.ru.mmultiple_charsets.gz] This patch adds
Thomas Roessler authored
340 safe_free ((void **) &p);
e577381 Initial revision
Thomas Roessler authored
341 return (0);
342 }
343
344 /* try to decode anything that looks like a valid RFC2047 encoded
345 * header field, ignoring RFC822 parsing rules
346 */
347 void rfc2047_decode (char *d, const char *s, size_t dlen)
348 {
349 const char *p, *q;
350 size_t n;
351 int found_encoded = 0;
352
353 dlen--; /* save room for the terminal nul */
354
355 while (*s && dlen > 0)
356 {
357 if ((p = strstr (s, "=?")) == NULL ||
358 (q = strchr (p + 2, '?')) == NULL ||
359 (q = strchr (q + 1, '?')) == NULL ||
360 (q = strstr (q + 1, "?=")) == NULL)
361 {
362 /* no encoded words */
363 if (d != s)
364 strfcpy (d, s, dlen + 1);
365 return;
366 }
367
368 if (p != s)
369 {
370 n = (size_t) (p - s);
371 /* ignore spaces between encoded words */
372 if (!found_encoded || strspn (s, " \t\r\n") != n)
373 {
374 if (n > dlen)
375 n = dlen;
376 if (d != s)
377 memcpy (d, s, n);
378 d += n;
379 dlen -= n;
380 }
381 }
382
383 rfc2047_decode_word (d, p, dlen);
384 found_encoded = 1;
385 s = q + 2;
386 n = strlen (d);
387 dlen -= n;
388 d += n;
389 }
390 *d = 0;
391 }
392
393 void rfc2047_decode_adrlist (ADDRESS *a)
394 {
395 while (a)
396 {
397 if (a->personal && strstr (a->personal, "=?") != NULL)
398 rfc2047_decode (a->personal, a->personal, strlen (a->personal) + 1);
399 a = a->next;
400 }
401 }
Something went wrong with that request. Please try again.