1 /*
2 ** Copyright 1998 - 1999 Double Precision, Inc. See COPYING for
3 ** distribution information.
4 */
5
6 #if HAVE_CONFIG_H
7 #include "config.h"
8 #endif
9 #include "rfc2045.h"
10 #include "rfc2045charset.h"
11 #if HAVE_UNISTD_H
12 #include <unistd.h>
13 #endif
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <ctype.h>
17 #include <string.h>
18 #if HAVE_STRINGS_H
19 #include <strings.h>
20 #endif
21 #ifdef __WINDOWS__
22 #define strcasecmp stricmp
23 #define strncasecmp strnicmp
24 #if (_MSC_VER < 1300)
25 #define write _write
26 #endif
27 #include <io.h>
28 #endif
29
30 /* $Id$ */
31
32 static char *rw_boundary_root;
33 static int rw_boundary_cnt;
34 static const char *rw_appname;
35
36 static FILE *fdin;
37 static int fdout;
38 static int (*fdout_func)(const char *, int, void *);
39 static void *fdout_arg;
40
41 static char fdout_buf[512];
42 static char *fdout_ptr;
43 static size_t fdout_left;
44
45 /* Quoted printable encoding */
46 static void qpe_start();
47 static int qpe_do(const char *, size_t, void *);
48 static void qpe_end();
49 static int conv_err;
50
fdout_flush()51 static int fdout_flush()
52 {
53 int n=fdout_ptr-fdout_buf;
54 int i=0;
55 char *p=fdout_buf;
56
57 while (n)
58 {
59 i=fdout_func ? (*fdout_func)(p, n, fdout_arg):
60 write(fdout, p, n);
61 if (i <= 0) return (-1);
62 p += i;
63 n -= i;
64 }
65 fdout_ptr=fdout_buf;
66 fdout_left=sizeof(fdout_buf);
67 return (0);
68 }
69
fdout_add(const char * p,size_t cnt)70 static int fdout_add(const char *p, size_t cnt)
71 {
72 while (cnt)
73 {
74 if (cnt < fdout_left)
75 {
76 memcpy(fdout_ptr, p, cnt);
77 fdout_ptr += cnt;
78 fdout_left -= cnt;
79 return (0);
80 }
81 if (fdout_left == 0)
82 {
83 if (fdout_flush()) return (-1);
84 continue;
85 }
86 memcpy(fdout_ptr, p, fdout_left);
87 p += fdout_left;
88 cnt -= fdout_left;
89 fdout_ptr += fdout_left;
90 fdout_left=0;
91 }
92 return (0);
93 }
94
do_8bit(const char * p,size_t cnt,void * ptr)95 static int do_8bit(const char *p, size_t cnt, void *ptr)
96 {
97 if (fdout_add(p, cnt))
98 conv_err=1;
99 return (0);
100 }
101
fdout_autoconverted(const char * oldte,const char * newte)102 static int fdout_autoconverted(const char *oldte, const char *newte)
103 {
104 if (fdout_add("X-Mime-Autoconverted: from ", 27) ||
105 fdout_add(oldte, strlen(oldte)) ||
106 fdout_add(" to ", 4) ||
107 fdout_add(newte, strlen(newte)) ||
108 (rw_appname && (fdout_add(" by ", 4) ||
109 fdout_add(rw_appname, strlen(rw_appname)))) ||
110 fdout_add("\n", 1)) return (-1);
111 return (0);
112 }
113
114 static int fdout_value(const char *);
115
fdout_attr(const struct rfc2045attr * a)116 static int fdout_attr(const struct rfc2045attr *a)
117 {
118 if (fdout_add(a->name, strlen(a->name))) return (-1);
119 if (a->value && (fdout_add("=", 1) || fdout_value(a->value)))
120 return (-1);
121 return (0);
122 }
123
fdout_value(const char * v)124 static int fdout_value(const char *v)
125 {
126 size_t i,j;
127
128 for (i=0; v[i]; i++)
129 {
130 if ( !isalnum((int)(unsigned char)v[i]) && v[i] != '-')
131 {
132 if (fdout_add("\"", 1)) return (-1);
133 for (j=i=0; v[i]; i++)
134 if (v[i] == '\\' || v[i] == '"')
135 {
136 if (fdout_add(v+j, i-j) ||
137 fdout_add("\\", 1))
138 return (-1);
139 j=i;
140 }
141 if (fdout_add(v+j, i-j) || fdout_add("\"", 1))
142 return (-1);
143 return (0);
144 }
145 }
146 return (fdout_add(v, i));
147 }
148
149 #define TE(p) ((p)->rw_transfer_encoding ? \
150 (p)->rw_transfer_encoding: (p)->content_transfer_encoding)
151
rwmime(struct rfc2045 * p)152 static int rwmime(struct rfc2045 *p)
153 {
154 static char mimever[]="Mime-Version: 1.0\n";
155 const char *te;
156 struct rfc2045attr *a;
157
158 if (!p->parent)
159 if (fdout_add(mimever, sizeof(mimever)-1)) return (-1);
160
161 if (p->content_type)
162 {
163 if (fdout_add("Content-Type: ", 14) ||
164 fdout_add(p->content_type, strlen(p->content_type)))
165 return (-1);
166
167 for (a=p->content_type_attr; a; a=a->next)
168 {
169 if (!a->name || strcmp(a->name, "boundary") == 0)
170 continue;
171 if ( fdout_add("; ", 2) ||
172 fdout_attr(a)) return (-1);
173 }
174 }
175
176 if (p->firstpart
177 && p->firstpart->next /* ADDED 8/30/99, see below */)
178 {
179 char buf[80];
180
181 ++rw_boundary_cnt;
182 sprintf(buf, "-%d", rw_boundary_cnt);
183 if ( fdout_add("; boundary=\"", 12) ||
184 fdout_add(rw_boundary_root, strlen(rw_boundary_root)) ||
185 fdout_add(buf, strlen(buf)) ||
186 fdout_add("\"", 1)) return (-1);
187 }
188 if (fdout_add("\n", 1)) return (-1);
189
190 /* Show content transfer encoding for top section, or if it's
191 ** different than the parent.
192 */
193 te=TE(p);
194 if (te && (!p->parent || strcmp(te, TE(p->parent))))
195 {
196 if (fdout_add("Content-Transfer-Encoding: ", 27) ||
197 fdout_add(te, strlen(te)) ||
198 fdout_add("\n", 1)) return (-1);
199 }
200 return (0);
201 }
202
dorw(struct rfc2045 * p)203 static int dorw(struct rfc2045 *p)
204 {
205 /* WTF STATIC??? */ int seen_mime=0;
206 char buf[256];
207 int c;
208 int bcnt;
209
210 if (fseek(fdin, p->startpos, SEEK_SET) == -1) return (-1);
211 if (p->parent)
212 {
213 seen_mime=1;
214 if (rwmime(p)) return (-1);
215 }
216 while (fgets(buf, sizeof(buf), fdin))
217 {
218 if (buf[0] == '\n') break;
219
220 if (RFC2045_ISMIME1DEF(p->mime_version) &&
221 strncasecmp(buf, "mime-version:", 13) == 0 &&
222 !seen_mime)
223 {
224 seen_mime=1;
225 rwmime(p);
226 if (strchr(buf, '\n') == NULL)
227 while ((c=getc(fdin)) >= 0 && c != '\n')
228 ;
229 while ((c=getc(fdin)) >= 0 && c != '\n' && isspace(c))
230 while ((c=getc(fdin)) >= 0 && c != '\n')
231 ;
232 if (c >= 0) ungetc(c, fdin);
233 continue;
234 }
235
236 if (!RFC2045_ISMIME1DEF(p->mime_version) || (
237 strncasecmp(buf, "mime-version:", 13) &&
238 strncasecmp(buf, "content-type:", 13) &&
239 strncasecmp(buf, "content-transfer-encoding:", 26))
240 )
241 {
242 do
243 {
244 do
245 {
246 if (fdout_add(buf, strlen(buf)))
247 return (-1);
248 } while (strchr(buf, '\n') == NULL &&
249 fgets(buf, sizeof(buf), fdin));
250
251 c=getc(fdin);
252 if (c >= 0) ungetc(c, fdin);
253 } while (c >= 0 && c != '\n' && isspace(c) &&
254 fgets(buf, sizeof(buf), fdin));
255 }
256 else
257 while ( (c=getc(fdin)) >= 0 &&
258 (ungetc(c, fdin), c) != '\n' && isspace(c))
259 {
260 while (fgets(buf, sizeof(buf), fdin) &&
261 strchr(buf, '\n') == NULL)
262 ;
263 }
264 }
265 if (RFC2045_ISMIME1DEF(p->mime_version))
266 {
267 if (!seen_mime)
268 if (rwmime(p)) return (-1);
269
270 if (!p->firstpart && p->rw_transfer_encoding)
271 if (fdout_autoconverted(p->content_transfer_encoding,
272 p->rw_transfer_encoding)) return (-1);
273 }
274
275 if (fdout_add("\n", 1)) return (-1);
276 if (fseek(fdin, p->startbody, SEEK_SET) == -1) return (-1);
277
278 /* For non-multipart section, just print the body */
279
280 if (!p->firstpart)
281 {
282 off_t ps=p->startbody;
283 int convmode=0;
284
285 if (p->rw_transfer_encoding)
286 {
287 if ( strcasecmp(p->rw_transfer_encoding,
288 "quoted-printable") == 0)
289 convmode=RFC2045_RW_7BIT;
290 else
291 convmode=RFC2045_RW_8BIT;
292 }
293
294 conv_err=0;
295 if (convmode == RFC2045_RW_7BIT)
296 {
297 qpe_start();
298 rfc2045_cdecode_start(p, &qpe_do, 0);
299 }
300
301 if (convmode == RFC2045_RW_8BIT)
302 {
303 rfc2045_cdecode_start(p, &do_8bit, 0);
304 }
305
306 while (ps < p->endbody)
307 {
308 int n;
309
310 if (p->endbody - ps > sizeof(buf))
311 n=sizeof(buf);
312 else n=p->endbody-ps;
313 n=fread(buf, 1, n, fdin);
314 if (n <= 0) return (-1);
315 if (convmode)
316 rfc2045_cdecode(p, buf, n);
317 else if (fdout_add(buf, n)) conv_err=1;
318 ps += n;
319 if (conv_err) break;
320 }
321 if (convmode == RFC2045_RW_7BIT)
322 {
323 rfc2045_cdecode_end(p);
324 qpe_end();
325 }
326 if (convmode == RFC2045_RW_8BIT)
327 {
328 rfc2045_cdecode_end(p);
329 }
330 if (conv_err) return (-1);
331 return (0);
332 }
333
334 bcnt=rw_boundary_cnt;
335
336 /* Sam 8/30/99 fix - handle message/rfc822:
337
338 --boundary
339 Content-Type: message/rfc822
340
341 --><-- we're here, DON'T add RFC2045MIMEMSG and rest of crap here
342 */
343 if (p->firstpart->next == 0)
344 {
345 int rc;
346
347 p->firstpart->parent=0;
348 rc=dorw(p->firstpart);
349 p->firstpart->parent=p;
350 return (rc);
351 }
352
353 if (fdout_add(RFC2045MIMEMSG, sizeof(RFC2045MIMEMSG)-1))
354 return (-1);
355 for (p=p->firstpart; p; p=p->next)
356 {
357 if (p->isdummy) continue;
358 sprintf(buf, "\n--%s-%d\n", rw_boundary_root, bcnt);
359 if (fdout_add(buf, strlen(buf))) return (-1);
360 if (dorw(p) != 0) return(-1);
361 }
362 sprintf(buf, "\n--%s-%d--\n", rw_boundary_root, bcnt);
363 if (fdout_add(buf, strlen(buf))) return (-1);
364 return (0);
365 }
366
367 static int rfc2045_rewrite_common(struct rfc2045 *, int, const char *);
368
rfc2045_rewrite(struct rfc2045 * p,int fdin_arg,int fdout_arg,const char * appname)369 int rfc2045_rewrite(struct rfc2045 *p, int fdin_arg, int fdout_arg,
370 const char *appname)
371 {
372 fdout=fdout_arg;
373 fdout_func=0;
374 return (rfc2045_rewrite_common(p, fdin_arg, appname));
375 }
376
rfc2045_rewrite_func(struct rfc2045 * p,int fdin_arg,int (* funcarg)(const char *,int,void *),void * funcargarg,const char * appname)377 int rfc2045_rewrite_func(struct rfc2045 *p, int fdin_arg,
378 int (*funcarg)(const char *, int, void *), void *funcargarg,
379 const char *appname)
380 {
381 fdout= -1;
382 fdout_func=funcarg;
383 fdout_arg=funcargarg;
384 return (rfc2045_rewrite_common(p, fdin_arg, appname));
385 }
386
rfc2045_rewrite_common(struct rfc2045 * p,int fdin_arg,const char * appname)387 static int rfc2045_rewrite_common(struct rfc2045 *p,
388 int fdin_arg, const char *appname)
389 {
390 int rc;
391 int fd=dup(fdin_arg);
392
393 if (fd < 0) return (-1);
394 rw_appname=appname;
395 fdin=fdopen(fd, "r");
396 if (!fdin)
397 {
398 close(fd);
399 return (-1);
400 }
401
402 fdout_ptr=fdout_buf;
403 fdout_left=sizeof(fdout_buf);
404
405 rw_boundary_root=rfc2045_mk_boundary(p, fd);
406 if (rw_boundary_root == 0)
407 rc= -1;
408 else
409 {
410 rw_boundary_cnt=1;
411 rc=dorw(p);
412 free(rw_boundary_root);
413 }
414 if (rc == 0 && fdout_ptr > fdout_buf)
415 rc=fdout_flush();
416 fclose(fdin);
417 return (rc);
418 }
419
420 static int qpe_pos;
421
qpe_start()422 static void qpe_start()
423 {
424 qpe_pos=0;
425 }
426
qpe_do(const char * p,size_t i,void * ptr)427 static int qpe_do(const char *p, size_t i, void *ptr)
428 {
429 size_t j,k;
430
431 if (conv_err) return (0);
432 for (j=k=0; j<i; j++)
433 {
434 if (p[j] == '\n')
435 {
436 if (fdout_add(p+k, j+1-k)) conv_err=1;
437 k=j+1;
438 qpe_pos=0;
439 continue;
440 }
441 if (qpe_pos >= 72)
442 {
443 if (fdout_add(p+k, j-k) ||
444 fdout_add("=\n", 2)) conv_err=1;
445 k=j;
446 qpe_pos=0;
447 }
448
449 if (p[j] < 32 || p[j] >= 127 || p[j] == '=')
450 {
451 char buf[3];
452 static char xdigit[16]="0123456789ABCDEF";
453 unsigned n= (unsigned char)p[j];
454
455 buf[0]='=';
456 buf[1]=xdigit[ n / 16];
457 buf[2]=xdigit[ n % 16];
458 if (fdout_add(p+k, j-k) ||
459 fdout_add(buf, 3)) conv_err=1;
460 qpe_pos += 2;
461 k=j+1;
462 }
463 ++qpe_pos;
464 }
465 if (fdout_add(p+k, j-k)) conv_err=1;
466 return (0);
467 }
468
qpe_end()469 static void qpe_end()
470 {
471 }
472