Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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.