Increase the header size so that forwarded mails with looooong
[infodrom/newmail] / rfc2047.c
1 /*
2     Copyright (c) 2006  Joey Schulze <joey@infodrom.org>
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 <stdlib.h>
20 #include <string.h>
21 #include <ctype.h>
22 #include "charset.h"
23
24 /*
25  * Index_hex and Index_64 imported from Mutt:handler.c
26  * decode_quotedprintable() and decode_base64() as well
27  */
28 int Index_hex[128] = {
29   -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
30   -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
31   -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
32   0, 1, 2, 3,  4, 5, 6, 7,  8, 9,-1,-1, -1,-1,-1,-1,
33   -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
34   -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
35   -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
36   -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1
37 };
38
39 int Index_64[128] = {
40   -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
41   -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
42   -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
43   52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
44   -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
45   15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
46   -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
47   41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
48 };
49 #define hexval(c) Index_hex[(unsigned int)(c)]
50 #define base64val(c) Index_64[(unsigned int)(c)]
51
52
53 /*
54  * Decode a string from quoted printable
55  */
56 char *decode_quotedprintable(char *buf)
57 {
58   char *inp, *outp;
59   char *endp;
60
61   endp = buf+strlen(buf);
62   
63   for (inp = outp = buf; inp < endp; inp++) {
64     if (*inp == '_')
65       *(outp++) = ' ';
66     else if (*inp == '=' && inp+2 < endp &&
67              (!(*(inp+1) & ~127) && hexval(*(inp+1)) != -1) &&
68              (!(*(inp+2) & ~127) && hexval(*(inp+2)) != -1)) {
69       *(outp++) = (hexval(*(inp+1)) << 4) | hexval(*(inp+2));
70       inp += 2;
71     } else
72       *(outp++) = *inp;
73   }
74   *outp = '\0';
75
76   return buf;
77 }
78
79 /*
80  * Decode a string from base43
81  */
82 char *decode_base64(char *buf)
83 {
84   char *inp, *outp;
85   char *endp;
86   int c, b = 0, k = 0;
87
88   endp = buf+strlen(buf);
89
90   for (inp = outp = buf; inp < endp; inp++) {
91     if (*inp == '=')
92       break;
93     if ((*inp & ~127) || (c = base64val(*inp)) == -1)
94       continue;
95     if (k + 6 >= 8)
96       {
97         k -= 2;
98         *(outp++) = b | (c >> k);
99         b = c << (8 - k);
100       }
101     else
102       {
103         b |= c << (k + 2);
104         k += 6;
105       }
106     *outp = '\0';
107   }
108
109   return buf;
110 }
111
112 /*
113  * converts a header line into the current character set if required
114  */
115 char *convert_header(char *buf)
116 {
117   char *charset;
118   int encoding;
119   char *inp, *outp, *encstart, *wordp, *encp, *endp;
120   char *decode;
121   size_t size;
122
123   endp = buf+strlen(buf);
124   inp = outp = buf;
125
126   while ((encstart = strstr (inp, "=?"))) {
127     if (inp == outp)
128       outp = encstart;
129     else {
130       memmove (outp, inp, encstart-inp-1);
131       outp += encstart-inp-1;
132     }
133     charset = encstart+2;
134
135     if ((encp = strchr (charset, '?')) == NULL) {
136       memmove (outp, inp, strlen(inp)+1);
137       break;
138     }
139
140     *encp = '\0';
141
142     if ((encp + 3 >= endp) || !strchr ("BbQq", *encp) || (*(encp+2) != '?')) {
143       memmove (outp, inp, strlen(inp)+1);
144       break;
145     }
146
147     encoding = toupper(*(encp+1));
148
149     inp = encp + 3;
150
151     if ((wordp = strstr (inp, "?=")) == NULL) {
152       memmove (outp, encstart, strlen(encstart)+1);
153       strcat (outp, "?");
154       encstart += strlen(encstart)+1;
155       memmove (outp, encstart, strlen(encstart)+1);
156       break;
157     }
158
159     *wordp = '\0';
160     wordp += 2;
161
162     switch (encoding) {
163     case 'B':
164       decode = decode_base64 (inp);
165       break;
166     case 'Q':
167       decode = decode_quotedprintable (inp);
168       break;
169     default:
170       decode = inp;
171       break;
172     }
173
174     size = wordp - outp -1;
175     decode = convert_word (charset, decode, outp, size);
176
177     memmove (outp, decode, strlen(decode));
178     outp += strlen(decode);
179
180     inp = wordp;
181   }
182
183   if (inp != outp)
184     memmove (outp, inp, strlen(inp)+1);
185
186   return buf;
187 }
188
189
190 /*
191  * Needs to be called with LANG=de_DE.ISO-8859-1
192
193 #include <stdio.h>
194
195 void test_rfc2047()
196 {
197   char *qp;
198   char *b64;
199   char *subject;
200
201   set_charset();
202
203   qp = strdup ("f=FCr");
204   printf ("\t%s -> ", qp);
205   b64 = decode_quotedprintable (qp);
206   printf ("%s\n", qp);
207   if (!strcmp (qp, "für"))
208     printf ("rfc2047.c: decode_quotedprintable() passed\n");
209   else
210     printf ("rfc2047.c: decode_quotedprintable() failed\n");
211   free (qp);
212
213   qp = strdup ("f=FCr_Umlaute_=EFm_Sub");
214   printf ("\t%s -> ", qp);
215   b64 = decode_quotedprintable (qp);
216   printf ("%s\n", qp);
217   if (!strcmp (qp, "für Umlaute ïm Sub"))
218     printf ("rfc2047.c: decode_quotedprintable() passed\n");
219   else
220     printf ("rfc2047.c: decode_quotedprintable() failed\n");
221   free (qp);
222
223   b64 = strdup ("ZvxyINxtbOT8dOs=");
224   printf ("\t%s -> ", b64);
225   b64 = decode_base64 (b64);
226   printf ("%s\n", b64);
227   if (!strcmp (b64, "für Ümläütë"))
228     printf ("rfc2047.c: decode_base64() passed\n");
229   else
230     printf ("rfc2047.c: decode_base64() failed\n");
231   free (b64);
232
233   subject = strdup ("Vorschlag =?ISO-8859-1?Q?f=FCr?= ein Eintrittskonzept");
234   printf ("\t%s\n", subject);
235   subject = convert_header (subject);
236   printf ("\t%s\n", subject);
237   if (!strcmp (subject, "Vorschlag für ein Eintrittskonzept"))
238     printf ("rfc2047.c: convert_header() passed\n");
239   else
240     printf ("rfc2047.c: convert_header() failed\n");
241   free (subject);
242
243   subject = strdup ("=?utf-8?q?Google=3A_Chat_f=C3=BCr_die_Ewigkeit?=");
244   printf ("\t%s\n", subject);
245   subject = convert_header (subject);
246   printf ("\t%s\n", subject);
247   if (!strcmp (subject, "Google: Chat für die Ewigkeit"))
248     printf ("rfc2047.c: convert_header() passed\n");
249   else
250     printf ("rfc2047.c: convert_header() failed\n");
251   free (subject);
252
253   subject = strdup ("=?iso-8859-1?B?ZvxyINxtbOT8dOs=?= testen");
254   printf ("\t%s\n", subject);
255   subject = convert_header (subject);
256   printf ("\t%s\n", subject);
257   if (!strcmp (subject, "für Ümläütë testen"))
258     printf ("rfc2047.c: convert_header() passed\n");
259   else
260     printf ("rfc2047.c: convert_header() failed\n");
261   free (subject);
262
263 }
264 */