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