* fix a buffer overrun in case of bad strings in vcards.
[citadel.git] / webcit / decode.c
1 #include "webcit.h"
2 #ifdef HAVE_ICONV
3
4 /*
5  * Wrapper around iconv_open()
6  * Our version adds aliases for non-standard Microsoft charsets
7  * such as 'MS950', aliasing them to names like 'CP950'
8  *
9  * tocode       Target encoding
10  * fromcode     Source encoding
11  * /
12 iconv_t ctdl_iconv_open(const char *tocode, const char *fromcode)
13 {
14         iconv_t ic = (iconv_t)(-1) ;
15         ic = iconv_open(tocode, fromcode);
16         if (ic == (iconv_t)(-1) ) {
17                 char alias_fromcode[64];
18                 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
19                         safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
20                         alias_fromcode[0] = 'C';
21                         alias_fromcode[1] = 'P';
22                         ic = iconv_open(tocode, alias_fromcode);
23                 }
24         }
25         return(ic);
26 }
27 */
28
29
30 inline char *FindNextEnd (char *bptr)
31 {
32         char * end;
33         /* Find the next ?Q? */
34         end = strchr(bptr + 2, '?');
35         if (end == NULL) return NULL;
36         if (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) && 
37             (*(end + 2) == '?')) {
38                 /* skip on to the end of the cluster, the next ?= */
39                 end = strstr(end + 3, "?=");
40         }
41         else
42                 /* sort of half valid encoding, try to find an end. */
43                 end = strstr(bptr, "?=");
44         return end;
45 }
46
47 /*
48  * Handle subjects with RFC2047 encoding such as:
49  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
50  */
51 void utf8ify_rfc822_string(char **buf) {
52         char *start, *end, *next, *nextend, *ptr;
53         char newbuf[1024];
54         char charset[128];
55         char encoding[16];
56         char istr[1024];
57         iconv_t ic = (iconv_t)(-1) ;
58         char *ibuf;                     /**< Buffer of characters to be converted */
59         char *obuf;                     /**< Buffer for converted characters */
60         size_t ibuflen;                 /**< Length of input buffer */
61         size_t obuflen;                 /**< Length of output buffer */
62         char *isav;                     /**< Saved pointer to input buffer */
63         char *osav;                     /**< Saved pointer to output buffer */
64         int passes = 0;
65         int i, len, delta;
66         int illegal_non_rfc2047_encoding = 0;
67
68         /* Sometimes, badly formed messages contain strings which were simply
69          *  written out directly in some foreign character set instead of
70          *  using RFC2047 encoding.  This is illegal but we will attempt to
71          *  handle it anyway by converting from a user-specified default
72          *  charset to UTF-8 if we see any nonprintable characters.
73          */
74         len = strlen(*buf);
75         for (i=0; i<len; ++i) {
76                 if (((*buf)[i] < 32) || ((*buf)[i] > 126)) {
77                         illegal_non_rfc2047_encoding = 1;
78                         i = len; /*< take a shortcut, it won't be more than one. */
79                 }
80         }
81         if (illegal_non_rfc2047_encoding) {
82                 StrBuf *default_header_charset;
83                 get_preference("default_header_charset", &default_header_charset);
84                 if ( (strcasecmp(ChrPtr(default_header_charset), "UTF-8")) && 
85                      (strcasecmp(ChrPtr(default_header_charset), "us-ascii")) ) {
86                         ctdl_iconv_open("UTF-8", ChrPtr(default_header_charset), &ic);
87                         if (ic != (iconv_t)(-1) ) {
88                                 ibuf = malloc(1024);
89                                 isav = ibuf;
90                                 safestrncpy(ibuf, *buf, 1023);
91                                 ibuflen = strlen(ibuf);
92                                 obuflen = 1024;
93                                 obuf = (char *) malloc(obuflen);
94                                 osav = obuf;
95                                 iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
96                                 osav[1023-obuflen] = 0;
97                                 free(*buf);
98                                 *buf = osav;
99                                 iconv_close(ic);
100                                 free(isav);
101                         }
102                 }
103         }
104
105         /* pre evaluate the first pair */
106         nextend = end = NULL;
107         len = strlen(*buf);
108         start = strstr(*buf, "=?");
109         if (start != NULL) 
110                 end = FindNextEnd (start);
111
112         while ((start != NULL) && (end != NULL))
113         {
114                 next = strstr(end, "=?");
115                 if (next != NULL)
116                         nextend = FindNextEnd(next);
117                 if (nextend == NULL)
118                         next = NULL;
119
120                 /* did we find two partitions */
121                 if ((next != NULL) && 
122                     ((next - end) > 2))
123                 {
124                         ptr = end + 2;
125                         while ((ptr < next) && 
126                                (isspace(*ptr) ||
127                                 (*ptr == '\r') ||
128                                 (*ptr == '\n') || 
129                                 (*ptr == '\t')))
130                                 ptr ++;
131                         /* did we find a gab just filled with blanks? */
132                         if (ptr == next)
133                         {
134                                 memmove (end + 2,
135                                          next,
136                                          len - (next - start));
137
138                                 /* now terminate the gab at the end */
139                                 delta = (next - end) - 2;
140                                 len -= delta;
141                                 (*buf)[len] = '\0';
142
143                                 /* move next to its new location. */
144                                 next -= delta;
145                                 nextend -= delta;
146                         }
147                 }
148                 /* our next-pair is our new first pair now. */
149                 start = next;
150                 end = nextend;
151         }
152
153         /* Now we handle foreign character sets properly encoded
154          * in RFC2047 format.
155          */
156         while (start=strstr((*buf), "=?"), end=FindNextEnd((start != NULL)? start : (*buf)),
157                 ((start != NULL) && (end != NULL) && (end > start)) )
158         {
159                 extract_token(charset, start, 1, '?', sizeof charset);
160                 extract_token(encoding, start, 2, '?', sizeof encoding);
161                 extract_token(istr, start, 3, '?', sizeof istr);
162
163                 ibuf = malloc(1024);
164                 isav = ibuf;
165                 if (!strcasecmp(encoding, "B")) {       /**< base64 */
166                         ibuflen = CtdlDecodeBase64(ibuf, istr, strlen(istr));
167                 }
168                 else if (!strcasecmp(encoding, "Q")) {  /**< quoted-printable */
169                         size_t len;
170                         long pos;
171                         
172                         len = strlen(istr);
173                         pos = 0;
174                         while (pos < len)
175                         {
176                                 if (istr[pos] == '_') istr[pos] = ' ';
177                                 pos++;
178                         }
179
180                         ibuflen = CtdlDecodeQuotedPrintable(ibuf, istr, len);
181                 }
182                 else {
183                         strcpy(ibuf, istr);             /**< unknown encoding */
184                         ibuflen = strlen(istr);
185                 }
186
187                 ctdl_iconv_open("UTF-8", charset, &ic);
188                 if (ic != (iconv_t)(-1) ) {
189                         obuflen = 1024;
190                         obuf = (char *) malloc(obuflen);
191                         osav = obuf;
192                         iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
193                         osav[1024-obuflen] = 0;
194
195                         end = start;
196                         end++;
197                         strcpy(start, "");
198                         remove_token(end, 0, '?');
199                         remove_token(end, 0, '?');
200                         remove_token(end, 0, '?');
201                         remove_token(end, 0, '?');
202                         strcpy(end, &end[1]);
203
204                         snprintf(newbuf, sizeof newbuf, "%s%s%s", *buf, osav, end);
205                         strcpy(*buf, newbuf);
206                         
207                         free(osav);
208                         iconv_close(ic);
209                 }
210                 else {
211                         end = start;
212                         end++;
213                         strcpy(start, "");
214                         remove_token(end, 0, '?');
215                         remove_token(end, 0, '?');
216                         remove_token(end, 0, '?');
217                         remove_token(end, 0, '?');
218                         strcpy(end, &end[1]);
219
220                         snprintf(newbuf, sizeof newbuf, "%s(unreadable)%s", *buf, end);
221                         strcpy(*buf, newbuf);
222                 }
223
224                 free(isav);
225
226                 /*
227                  * Since spammers will go to all sorts of absurd lengths to get their
228                  * messages through, there are LOTS of corrupt headers out there.
229                  * So, prevent a really badly formed RFC2047 header from throwing
230                  * this function into an infinite loop.
231                  */
232                 ++passes;
233                 if (passes > 20) return;
234         }
235
236 }
237 #else
238 inline void utf8ify_rfc822_string(char *a){};
239
240 #endif
241
242
243
244
245 /**
246  * \brief       RFC2047-encode a header field if necessary.
247  *              If no non-ASCII characters are found, the string
248  *              will be copied verbatim without encoding.
249  *
250  * \param       target          Target buffer.
251  * \param       maxlen          Maximum size of target buffer.
252  * \param       source          Source string to be encoded.
253  * \param       SourceLen       Length of the source string
254  * \returns     encoded length; -1 if non success.
255  */
256 int webcit_rfc2047encode(char *target, int maxlen, char *source, long SourceLen)
257 {
258         const char headerStr[] = "=?UTF-8?Q?";
259         int need_to_encode = 0;
260         int i = 0;
261         int len;
262         unsigned char ch;
263
264         if ((source == NULL) || 
265             (target == NULL) ||
266             (SourceLen > maxlen)) return -1;
267
268         while ((!IsEmptyStr (&source[i])) && 
269                (need_to_encode == 0) &&
270                (i < SourceLen) ) {
271                 if (((unsigned char) source[i] < 32) || 
272                     ((unsigned char) source[i] > 126)) {
273                         need_to_encode = 1;
274                 }
275                 i++;
276         }
277
278         if (!need_to_encode) {
279                 memcpy (target, source, SourceLen);
280                 target[SourceLen] = '\0';
281                 return SourceLen;
282         }
283         
284         if (sizeof (headerStr + SourceLen + 2) > maxlen)
285                 return -1;
286         memcpy (target, headerStr, sizeof (headerStr));
287         len = sizeof (headerStr) - 1;
288         for (i=0; (i < SourceLen) && (len + 3< maxlen) ; ++i) {
289                 ch = (unsigned char) source[i];
290                 if ((ch < 32) || (ch > 126) || (ch == 61)) {
291                         sprintf(&target[len], "=%02X", ch);
292                         len += 3;
293                 }
294                 else {
295                         sprintf(&target[len], "%c", ch);
296                         len ++;
297                 }
298         }
299         
300         if (len + 2 < maxlen) {
301                 strcat(&target[len], "?=");
302                 len +=2;
303                 return len;
304         }
305         else
306                 return -1;
307 }
308