1 /*
2 ** Copyright 1998 - 2018 Double Precision, Inc.  See COPYING for
3 ** distribution information.
4 */
5 
6 #if	HAVE_CONFIG_H
7 #include "rfc2045_config.h"
8 #endif
9 #include	<sys/types.h>
10 #include	<sys/stat.h>
11 #include	<time.h>
12 #include	<stdio.h>
13 #include	<errno.h>
14 #include	<string.h>
15 #include	<langinfo.h>
16 
17 #if	HAVE_STRINGS_H
18 #include	<strings.h>
19 #endif
20 
21 #if	HAVE_LOCALE_H
22 #include	<locale.h>
23 #endif
24 
25 #include	<stdlib.h>
26 #include	<ctype.h>
27 #include	<pwd.h>
28 #include	<fcntl.h>
29 #include	<signal.h>
30 #include	"rfc2045.h"
31 #include	"rfc822/rfc822.h"
32 #include	"rfc822/rfc2047.h"
33 #include	"rfc2045charset.h"
34 #include	<courier-unicode.h>
35 
36 #if HAVE_UNISTD_H
37 #include	<unistd.h>
38 #endif
39 #if HAVE_SYS_WAIT_H
40 #include	<sys/wait.h>
41 #endif
42 #include	"numlib/numlib.h"
43 
44 #if     HAS_GETHOSTNAME
45 #else
46 int gethostname(const char *, size_t);
47 #endif
48 
49 extern int rfc2045_in_reformime;
50 
51 static const char *defchset;
52 
53 
rfc2045_error(const char * errmsg)54 void rfc2045_error(const char *errmsg)
55 {
56 	fprintf(stderr, "reformime: %s\n", errmsg);
57 	exit(1);
58 }
59 
do_print_structure(struct rfc2045 * p,struct rfc2045id * id,void * ptr)60 static void do_print_structure(struct rfc2045 *p, struct rfc2045id *id, void *ptr)
61 {
62 	ptr=p;
63 
64 	while (id)
65 	{
66 		printf("%d%c", id->idnum, id->next ? '.':'\n');
67 		id=id->next;
68 	}
69 }
70 
decode_to_file(const char * p,size_t n,void * ptr)71 static int decode_to_file(const char *p, size_t n, void *ptr)
72 {
73 FILE	*fp=(FILE *)ptr;
74 
75 	while (n)
76 	{
77 		--n;
78 		if (putc((int)(unsigned char)*p++, fp) == EOF)
79 		{
80 			perror("write");
81 			exit(1);
82 		}
83 	}
84 	return (0);
85 }
86 
usage()87 void usage()
88 {
89 	fprintf(stderr, "Usage: reformime [options]\n");
90 	fprintf(stderr, "    -d - parse a delivery status notification.\n");
91 	fprintf(stderr, "    -e - extract contents of MIME section.\n");
92 	fprintf(stderr, "    -x - extract MIME section to a file.\n");
93 	fprintf(stderr, "    -X - pipe MIME section to a program.\n");
94 	fprintf(stderr, "    -i - show MIME info.\n");
95 	fprintf(stderr, "    -s n.n.n.n[,n.n.n.n]* - specify MIME section(s).\n");
96 	fprintf(stderr, "    -r - rewrite message, filling in missing MIME headers.\n");
97 	fprintf(stderr, "    -r7 - also convert 8bit/raw encoding to quoted-printable, if possible.\n");
98 	fprintf(stderr, "    -r8 - also convert quoted-printable encoding to 8bit, if possible.\n");
99 	fprintf(stderr, "    -rU - convert quoted-printable encoding to 8bit, unconditionally.\n");
100 	fprintf(stderr, "    -c charset - default charset for rewriting, -o, and -O.\n");
101 	fprintf(stderr, "    -m [file] [file]... - create a MIME message digest.\n");
102 	fprintf(stderr, "    -h \"header\" - decode RFC 2047-encoded header.\n");
103 	fprintf(stderr, "    -o \"header\" - encode unstructured header using RFC 2047.\n");
104 	fprintf(stderr, "    -O \"header\" - encode address list header using RFC 2047.\n");
105 
106 	exit(1);
107 }
108 
tempname(const char * tempdir)109 static char *tempname(const char *tempdir)
110 {
111 char	pidbuf[NUMBUFSIZE], timebuf[NUMBUFSIZE], hostnamebuf[256];
112 static unsigned counter=0;
113 time_t	t;
114 char	*p;
115 
116 	libmail_str_pid_t(getpid(), pidbuf);
117 	time(&t);
118 	libmail_str_time_t(t, timebuf);
119 	hostnamebuf[sizeof(hostnamebuf)-1]=0;
120 	if (gethostname(hostnamebuf, sizeof(hostnamebuf)))
121 		hostnamebuf[0]=0;
122 	p=malloc(strlen(tempdir)+strlen(pidbuf)+strlen(timebuf)+
123 		strlen(hostnamebuf)+100);
124 	if (!p)	return (0);
125 	sprintf(p, "%s/%s.%s-%u.%s", tempdir, timebuf, pidbuf, counter++,
126 		hostnamebuf);
127 	return (p);
128 }
129 
read_message()130 struct rfc2045 *read_message()
131 {
132 char	buf[BUFSIZ];
133 struct	rfc2045 *p=rfc2045_alloc_ac();
134 FILE	*tempfp=0;
135 int	l;
136 
137 	if (fseek(stdin, 0L, SEEK_END) < 0 ||
138 		fseek(stdin, 0L, SEEK_SET) < 0)	/* Pipe, save to temp file */
139 	{
140 		tempfp=tmpfile();
141 	}
142 
143 	while ((l=fread(buf, 1, sizeof(buf), stdin)) > 0)
144 	{
145 
146 		rfc2045_parse(p, buf, l);
147 		if (tempfp && fwrite(buf, l, 1, tempfp) != 1)
148 		{
149 			perror("fwrite");
150 			exit(1);
151 		}
152 	}
153 	rfc2045_parse_partial(p);
154 
155 	if (tempfp)	/* Replace stdin */
156 	{
157 		dup2(fileno(tempfp), 0);
158 		fclose(tempfp);
159 	}
160 	return (p);
161 }
162 
print_structure(struct rfc2045 * p)163 void print_structure(struct rfc2045 *p)
164 {
165 	rfc2045_decode(p, &do_print_structure, 0);
166 }
167 
notfound(const char * p)168 static void notfound(const char *p)
169 {
170 	fprintf(stderr, "reformime: MIME section %s not found.\n", p);
171 	exit(1);
172 }
173 
do_print_info(struct rfc2045 * s)174 static void do_print_info(struct rfc2045 *s)
175 {
176 const char *content_type, *transfer_encoding, *charset;
177 off_t start, end, body;
178 char *content_name;
179 off_t nlines, nbodylines;
180 const char *p;
181 
182 char *disposition_name, *disposition_filename;
183 
184 	rfc2045_mimeinfo(s, &content_type, &transfer_encoding, &charset);
185 	rfc2045_mimepos(s, &start, &end, &body, &nlines, &nbodylines);
186 
187 	if (rfc2231_udecodeType(s, "name", NULL, &content_name) < 0
188 	    && (content_name=strdup("")) == NULL)
189 	{
190 		perror("malloc");
191 		exit(1);
192 	}
193 
194 	printf("content-type: %s\n", content_type);
195 	if (*content_name)
196 	{
197 		printf("content-name: %s\n", content_name);
198 	}
199 	free(content_name);
200 
201 	printf("content-transfer-encoding: %s\n", transfer_encoding);
202 	printf("charset: %s\n", charset);
203 	if (s->content_disposition && *s->content_disposition)
204 		printf("content-disposition: %s\n", s->content_disposition);
205 
206 	if ((rfc2231_udecodeDisposition(s, "name", NULL, &disposition_name) < 0
207 	     && (disposition_name=strdup("")) == NULL)
208 	    ||
209 	    (rfc2231_udecodeDisposition(s, "filename", NULL,
210 					&disposition_filename) < 0
211 	     && (disposition_filename=strdup("")) == NULL))
212 	{
213 		perror("malloc");
214 		exit(1);
215 	}
216 
217 	if (*disposition_name)
218 		printf("content-disposition-name: %s\n", disposition_name);
219 
220 	free(disposition_name);
221 
222 	if (*disposition_filename)
223 	{
224 		printf("content-disposition-filename: %s\n",
225 		       disposition_filename);
226 	}
227 	free(disposition_filename);
228 
229 	if (*(p=rfc2045_content_id(s)))
230 		printf("content-id: <%s>\n", p);
231 	if (*(p=rfc2045_content_description(s)))
232 	{
233 		char *s=rfc822_display_hdrvalue_tobuf("content-description",
234 						      p,
235 						      defchset,
236 						      NULL,
237 						      NULL);
238 
239 		if (!s)
240 		{
241 			perror("rfc2047_decode_unicode");
242 			exit(1);
243 		}
244 		printf("content-description: %s\n", s);
245 		free(s);
246 	}
247 	if (*(p=rfc2045_content_language(s)))
248 		printf("content-language: %s\n", p);
249 	if (*(p=rfc2045_content_md5(s)))
250 		printf("content-md5: %s\n", p);
251 
252 	printf("starting-pos: %lu\n", (unsigned long)start);
253 	printf("starting-pos-body: %lu\n", (unsigned long)body);
254 	printf("ending-pos: %lu\n", (unsigned long)end);
255 	printf("line-count: %lu\n", (unsigned long)nlines);
256 	printf("body-line-count: %lu\n", (unsigned long)nbodylines);
257 }
258 
do_print_info_multiple(struct rfc2045 * p,struct rfc2045id * id,void * ptr)259 static void do_print_info_multiple(struct rfc2045 *p, struct rfc2045id *id,
260 		void *ptr)
261 {
262 	printf("section: ");
263 	do_print_structure(p, id, ptr);
264 	do_print_info(p);
265 	printf("\n");
266 }
267 
print_info(struct rfc2045 * p,const char * mimesection)268 void print_info(struct rfc2045 *p, const char *mimesection)
269 {
270 struct	rfc2045 *s;
271 
272 	if (mimesection)
273 	{
274 		s=rfc2045_find(p, mimesection);
275 		if (!s)
276 			notfound(mimesection);
277 		printf("section: %s\n", mimesection);
278 		do_print_info(s);
279 		return;
280 	}
281 	rfc2045_decode(p, &do_print_info_multiple, 0);
282 }
283 
do_print_section(struct rfc2045 * s,FILE * fp)284 static void do_print_section(struct rfc2045 *s, FILE *fp)
285 {
286 off_t start, end, body;
287 off_t nlines;
288 off_t nbodylines;
289 
290 	rfc2045_mimepos(s, &start, &end, &body, &nlines, &nbodylines);
291 
292 	if (fseek(stdin, body, SEEK_SET) == -1)
293 	{
294 		perror("fseek");
295 		exit(1);
296 	}
297 
298 	rfc2045_cdecode_start(s, &decode_to_file, fp);
299 	while (body < end)
300 	{
301 	char	buf[BUFSIZ];
302 	size_t	n=sizeof(buf);
303 
304 		if ((off_t)n > end-body)	n=end-body;
305 		n=fread(buf, 1, n, stdin);
306 		if (n == 0)
307 		{
308 			perror("fread");
309 			exit(1);
310 		}
311 		rfc2045_cdecode(s, buf, n);
312 		body += n;
313 	}
314 	rfc2045_cdecode_end(s);
315 }
316 
print_decode(struct rfc2045 * p,const char * mimesection)317 void print_decode(struct rfc2045 *p, const char *mimesection)
318 {
319 struct	rfc2045 *s;
320 
321 	if (!mimesection)
322 		usage();
323 
324 	s=rfc2045_find(p, mimesection);
325 	if (!s)
326 		notfound(mimesection);
327 
328 	do_print_section(s, stdout);
329 }
330 
rewrite(struct rfc2045 * p,int rwmode)331 void rewrite(struct rfc2045 *p, int rwmode)
332 {
333 	struct rfc2045src *src;
334 
335 	rfc2045_ac_check(p, rwmode);
336 
337 	src=rfc2045src_init_fd(fileno(stdin));
338 
339 	if (src == NULL || rfc2045_rewrite(p, src, fileno(stdout),
340 		"reformime (" RFC2045PKG " " RFC2045VER ")"))
341 	{
342 		perror("reformime");
343 		exit(1);
344 	}
345 	rfc2045src_deinit(src);
346 }
347 
get_suitable_filename(struct rfc2045 * r,const char * pfix,int ignore_filename)348 static char *get_suitable_filename(struct rfc2045 *r, const char *pfix,
349 	int ignore_filename)
350 {
351 char *disposition_name;
352 char *disposition_filename;
353 char	*filename_buf;
354 char *content_name;
355 char	*p, *q;
356 char	*dyn_disp_name=0;
357 
358 const char *disposition_filename_s;
359 
360 	if (rfc2231_udecodeDisposition(r, "name", NULL, &disposition_name) < 0)
361 		disposition_name=NULL;
362 
363 	if (rfc2231_udecodeDisposition(r, "filename", NULL,
364 				       &disposition_filename) < 0)
365 		disposition_filename=NULL;
366 
367 	if (rfc2231_udecodeType(r, "name", NULL,
368 				&content_name) < 0)
369 		content_name=NULL;
370 
371 	disposition_filename_s=disposition_filename;
372 
373 	if (!disposition_filename_s || !*disposition_filename_s)
374 		disposition_filename_s=disposition_name;
375 	if (!disposition_filename_s || !*disposition_filename_s)
376 		disposition_filename_s=content_name;
377 
378 	filename_buf=strdup(disposition_filename_s ? disposition_filename_s:"");
379 
380 	if (!filename_buf)
381 	{
382 		perror("strdup");
383 		exit(1);
384 	}
385 
386 	if (content_name)		free(content_name);
387 	if (disposition_name)		free(disposition_name);
388 	if (disposition_filename)	free(disposition_filename);
389 
390 	if (strlen(filename_buf) > 32)
391 	{
392 		p=filename_buf;
393 		q=filename_buf + strlen(filename_buf)-32;
394 		while ( (*p++ = *q++) != 0)
395 			;
396 	}
397 
398 	/* Strip leading/trailing spaces */
399 
400 	p=filename_buf;
401 	while (*p && isspace((int)(unsigned char)*p))
402 		++p;
403 
404 	q=filename_buf;
405 	while ((*q=*p) != 0)
406 	{
407 		++p;
408 		++q;
409 	}
410 
411 	for (p=q=filename_buf; *p; p++)
412 		if (!isspace((int)(unsigned char)*p))
413 			q=p+1;
414 	*q=0;
415 
416 	disposition_filename_s=filename_buf;
417 
418 	if (ignore_filename)
419 	{
420 	char	numbuf[NUMBUFSIZE];
421 	static size_t counter=0;
422 	const char *p=libmail_str_size_t(++counter, numbuf);
423 
424 		dyn_disp_name=malloc(strlen(disposition_filename_s)
425 			+ strlen(p)+2);
426 		if (!dyn_disp_name)
427 		{
428 			perror("malloc");
429 			exit(1);
430 		}
431 		disposition_filename_s=strcat(strcat(strcpy(
432 			dyn_disp_name, p), "-"),
433 			disposition_filename_s);
434 	}
435 	else if (!disposition_filename_s || !*disposition_filename_s)
436 	{
437 		dyn_disp_name=tempname(".");
438 		disposition_filename_s=dyn_disp_name+2;	/* Skip over ./ */
439 	}
440 
441 	p=malloc((pfix ? strlen(pfix):0)+strlen(disposition_filename_s)+1);
442 	if (!p)
443 	{
444 		perror("malloc");
445 		exit(1);
446 	}
447 	*p=0;
448 	if (pfix)	strcpy(p, pfix);
449 	q=p+strlen(p);
450 	for (strcpy(q, disposition_filename_s); *q; q++)
451 		if (!isalnum(*q) && *q != '.' && *q != '-')
452 			*q='_';
453 
454 	if (dyn_disp_name)	free(dyn_disp_name);
455 
456 	if (!pfix)
457 	{
458         const char *content_type_s;
459         const char *content_transfer_encoding_s;
460         const char *charset_s;
461 	int c;
462 	static char filenamebuf[256];
463 	char	*t;
464 	FILE	*tty;
465 
466 		if ((tty=fopen("/dev/tty", "r+")) == 0)
467 		{
468 			perror("/dev/tty");
469 			exit(1);
470 		}
471 
472 		rfc2045_mimeinfo(r, &content_type_s,
473 			&content_transfer_encoding_s, &charset_s);
474 
475 		fprintf (tty, "Extract %s? ", content_type_s);
476 		fflush(tty);
477 		c=getc(tty);
478 		if (c != '\n' && c != EOF)
479 		{
480 		int	cc;
481 
482 			while ((cc=getc(tty)) != '\n' && cc != EOF)
483 				;
484 		}
485 		if (c != 'y' && c != 'Y')
486 		{
487 			free(p);
488 			fclose(tty);
489 			free(filename_buf);
490 			return (0);
491 		}
492 		fprintf (tty, "Filename [%s]: ", p);
493 		if (fgets(filenamebuf, sizeof(filenamebuf)-1, tty) == NULL)
494 			filenamebuf[0]=0;
495 
496 		fclose(tty);
497 		t=strchr(filenamebuf, '\n');
498 		if (t)	*t=0;
499 		else
500 		{
501 			fprintf(stderr, "Filename too long.\n");
502 			exit(1);
503 		}
504 		if (filenamebuf[0])
505 		{
506 			free(p);
507 			p=strdup(filenamebuf);
508 			if (!p)
509 			{
510 				perror("malloc");
511 				exit(1);
512 			}
513 		}
514 	}
515 	free(filename_buf);
516 	return (p);
517 }
518 
extract_file(struct rfc2045 * p,const char * filename,int argc,char ** argv)519 static void extract_file(struct rfc2045 *p,
520 	const char *filename, int argc, char **argv)
521 {
522 char	*f;
523 FILE	*fp;
524 int	ignore=0;
525 
526 	for (;;)
527 	{
528 	int	fd;
529 
530 		f=get_suitable_filename(p, filename, ignore);
531 		if (!f)	return;
532 
533 		fd=open(f, O_WRONLY|O_CREAT|O_EXCL, 0666);
534 		if (fd < 0)
535 		{
536 			if (errno == EEXIST)
537 			{
538 				printf("%s exists.\n", f);
539 				free(f);
540 				ignore=1;
541 				continue;
542 			}
543 
544 			perror(f);
545 			exit(1);
546 		}
547 		fp=fdopen(fd, "w");
548 		if (!fp)
549 		{
550 			perror("fdopen");
551 			exit(1);
552 		}
553 		break;
554 	}
555 
556 	do_print_section(p, fp);
557 	if (fflush(fp) || ferror(fp))
558 	{
559 		perror("write");
560 		exit(1);
561 	}
562 	fclose(fp);
563 	free(f);
564 }
565 
extract_pipe(struct rfc2045 * p,const char * filename,int argc,char ** argv)566 static void extract_pipe(struct rfc2045 *p,
567 	const char *filename,
568 	int argc, char **argv)
569 {
570 char	*f=get_suitable_filename(p, "FILENAME=", 0);
571 int	pipefd[2];
572 pid_t	pid, p2;
573 FILE	*fp;
574 int	waitstat;
575 
576 	if (argc == 0)
577 	{
578 		fprintf(stderr, "reformime: Invalid -X option.\n");
579 		exit(1);
580 	}
581 
582 	if (pipe(pipefd))
583 	{
584 		perror("pipe");
585 		exit(1);
586 	}
587 
588 	if ((fp=fdopen(pipefd[1], "w")) == 0)
589 	{
590 		perror("fdopen");
591 		exit(1);
592 	}
593 
594 	while ((pid=fork()) == -1)
595 	{
596 		sleep(2);
597 	}
598 
599 	if (pid == 0)
600 	{
601         const char *content_type_s;
602         const char *content_transfer_encoding_s;
603         const char *charset_s;
604 
605 		if (!f)	f="FILENAME=attachment.dat";
606 		putenv(f);
607 		rfc2045_mimeinfo(p, &content_type_s,
608 			&content_transfer_encoding_s, &charset_s);
609 		f=malloc(strlen(content_type_s)
610 			+sizeof("CONTENT_TYPE="));
611 		if (!f)
612 		{
613 			perror("malloc");
614 			exit(1);
615 		}
616 		strcat(strcpy(f, "CONTENT_TYPE="), content_type_s);
617 		putenv(f);
618 		dup2(pipefd[0], 0);
619 		close(pipefd[0]);
620 		close(pipefd[1]);
621 		execv(argv[0], argv);
622 		perror("exec");
623 		_exit(1);
624 	}
625 	close(pipefd[0]);
626 	signal(SIGPIPE, SIG_IGN);
627 	do_print_section(p, fp);
628 	signal(SIGPIPE, SIG_DFL);
629 	fclose(fp);
630 	close(pipefd[1]);
631 
632 	while ((p2=wait(&waitstat)) != pid && p2 != -1)
633 		;
634 	free(f);
635 
636 	if ((p2 == pid) && WIFEXITED(waitstat))
637 	{
638 		if (WEXITSTATUS(waitstat) != 0)
639 		{
640 			fprintf(stderr, "reformime: %s exited with status %d.\n",
641 				argv[0], WEXITSTATUS(waitstat));
642 			exit(WEXITSTATUS(waitstat) + 20);
643 		}
644 	}
645 }
646 
extract_section(struct rfc2045 * top_rfcp,const char * mimesection,const char * extract_filename,int argc,char ** argv,void (* extract_func)(struct rfc2045 *,const char *,int,char **))647 static void extract_section(struct rfc2045 *top_rfcp, const char *mimesection,
648 	const char *extract_filename, int argc, char **argv,
649 	void	(*extract_func)(struct rfc2045 *, const char *,
650 		int, char **))
651 {
652 	if (mimesection)
653 	{
654 		top_rfcp=rfc2045_find(top_rfcp, mimesection);
655 		if (!top_rfcp)
656 			notfound(mimesection);
657 		if (top_rfcp->firstpart)
658 		{
659 			fprintf(stderr, "reformime: MIME section %s is a compound section.\n", mimesection);
660 			exit(1);
661 		}
662 		(*extract_func)(top_rfcp, extract_filename, argc, argv);
663 		return;
664 	}
665 
666 	/* Recursive */
667 
668 	if (top_rfcp->firstpart)
669 	{
670 		for (top_rfcp=top_rfcp->firstpart; top_rfcp;
671 			top_rfcp=top_rfcp->next)
672 			extract_section(top_rfcp, mimesection,
673 				extract_filename, argc, argv, extract_func);
674 		return;
675 	}
676 
677 	if (!top_rfcp->isdummy)
678 		(*extract_func)(top_rfcp, extract_filename, argc, argv);
679 }
680 
print_dsn_recip(char * addr,char * action)681 static void print_dsn_recip(char *addr, char *action)
682 {
683 char *p, *q;
684 
685 	if (!action || !addr)
686 	{
687 		if (action)	free(action);
688 		if (addr)	free(addr);
689 		return;
690 	}
691 
692 	for (p=action; *p; ++p)
693 		*p=tolower((int)(unsigned char)*p);
694 
695 	for (p=addr; *p && isspace((int)(unsigned char)*p); ++p)
696 		;
697 
698 	if (strncasecmp(p, "rfc822;", 7) &&
699 	    strncasecmp(p, "utf-8;", 6))
700 	{
701 		free(action);
702 		free(addr);
703 		return;
704 	}
705 	for (q=action; *q && isspace((int)(unsigned char)*q); ++q)
706 		;
707 
708 	p=strchr(p, ';')+1;
709 	while (*p && isspace((int)(unsigned char)*p))
710 		++p;
711 	printf("%s %s\n", q, p);
712 	free(action);
713 	free(addr);
714 }
715 
dsn(struct rfc2045 * p,int do_orig)716 static void dsn(struct rfc2045 *p, int do_orig)
717 {
718 const char *content_type_s;
719 const char *content_transfer_encoding_s;
720 const char *charset_s;
721 off_t start_pos, end_pos, start_body;
722 off_t dummy;
723 const char *q;
724 char	buf[BUFSIZ];
725 unsigned i;
726 int	ch;
727 char *recip;
728 char *action;
729 char *orecip;
730 
731 	rfc2045_mimeinfo(p, &content_type_s, &content_transfer_encoding_s,
732 		&charset_s);
733 	if (strcasecmp(content_type_s, "multipart/report") ||
734 		(q=rfc2045_getattr(p->content_type_attr, "report-type")) == 0 ||
735 		strcasecmp(q, "delivery-status") ||
736 		!p->firstpart || !p->firstpart->next ||
737 		!p->firstpart->next->next)
738 		_exit(1);
739 	p=p->firstpart->next->next;
740 	rfc2045_mimeinfo(p, &content_type_s, &content_transfer_encoding_s,
741 		&charset_s);
742 	rfc2045_mimepos(p, &start_pos, &end_pos, &start_body, &dummy, &dummy);
743 	if (!rfc2045_delivery_status_content_type(content_type_s) ||
744 		fseek(stdin, start_body, SEEK_SET) == -1)
745 		_exit(1);
746 
747 	i=0;
748 	recip=0;
749 	orecip=0;
750 	action=0;
751 	while (start_body < end_pos)
752 	{
753 		if ((ch=getchar()) == EOF)	break;
754 		++start_body;
755 		if (i < sizeof(buf)-1)
756 			buf[i++]= ch;
757 		if (ch != '\n')	continue;
758 		ch=getchar();
759 		if (ch != EOF)	ungetc(ch, stdin);
760 		if (ch != '\n' && isspace((int)(unsigned char)ch))
761 			continue;
762 		buf[i-1]=0;
763 		if (buf[0] == 0)
764 		{
765 			if (orecip)
766 			{
767 				if (recip)	free(recip);
768 				recip=orecip;
769 				orecip=0;
770 			}
771 			print_dsn_recip(recip, action);
772 			recip=0;
773 			action=0;
774 		}
775 		if (strncasecmp(buf, "Final-Recipient:", 16) == 0 &&
776 			recip == 0)
777 		{
778 			recip=strdup(buf+16);
779 			if (!recip)
780 			{
781 				perror("strdup");
782 				exit(2);
783 			}
784 		}
785 		if (strncasecmp(buf, "Original-Recipient:", 19) == 0 &&
786 			orecip == 0 && do_orig)
787 		{
788 			orecip=strdup(buf+19);
789 			if (!orecip)
790 			{
791 				perror("strdup");
792 				exit(2);
793 			}
794 		}
795 		if (strncasecmp(buf, "Action:", 7) == 0 && action == 0)
796 		{
797 			action=strdup(buf+7);
798 			if (!action)
799 			{
800 				perror("strdup");
801 				exit(2);
802 			}
803 		}
804 		i=0;
805 	}
806 	if (orecip)
807 	{
808 		if (recip)	free(recip);
809 		recip=orecip;
810 		orecip=0;
811 	}
812 	print_dsn_recip(recip, action);
813 }
814 
815 static void mimedigest1(int, char **);
816 static char mimebuf[BUFSIZ];
817 
mimedigest(int argc,char ** argv)818 static void mimedigest(int argc, char **argv)
819 {
820 char	*p;
821 struct filelist { struct filelist *next; char *fn; } *first=0, *last=0;
822 unsigned pcnt=0;
823 char	**l;
824 
825 	if (argc > 0)
826 	{
827 		mimedigest1(argc, argv);
828 		return;
829 	}
830 
831 	while (fgets(mimebuf, sizeof(mimebuf), stdin))
832 	{
833 	struct	filelist *q;
834 
835 		if ((p=strchr(mimebuf, '\n')) != 0)	*p=0;
836 		q=malloc(sizeof(struct filelist));
837 		if (!q || !(q->fn=strdup(mimebuf)))
838 		{
839 			perror("malloc");
840 			exit(1);
841 		}
842 
843 		if (last)	last->next=q;
844 		else	first=q;
845 		last=q;
846 		q->next=0;
847 		++pcnt;
848 	}
849 	if (pcnt == 0)	return;
850 
851 	if ( (l=malloc(sizeof (char *) * pcnt)) == 0)
852 	{
853 		perror("malloc");
854 	}
855 	pcnt=0;
856 
857 	for (last=first; last; last=last->next)
858 		l[pcnt++]=last->fn;
859 
860 	mimedigest1(pcnt, l);
861 	free(l);
862 	while(first)
863 	{
864 		last=first->next;
865 		free(first->fn);
866 		free(first);
867 		first=last;
868 	}
869 }
870 
mimedigest1(int argc,char ** argv)871 static void mimedigest1(int argc, char **argv)
872 {
873 	time_t	t;
874 	char	boundarybuf[200];
875 	unsigned boundarycnt=0;
876 	int	i;
877 	FILE	*fp;
878 	int	*utf8;
879 	if (argc == 0)
880 		return;
881 
882 	time (&t);
883 
884 	utf8=malloc(sizeof(int)*argc);
885 
886 	/* Search for a suitable boundary */
887 
888 	do
889 	{
890 	int	l;
891 
892 		sprintf(boundarybuf, "reformime_%lu_%lu_%u",
893 			(unsigned long)t,
894 			(unsigned long)getpid(),
895 			++boundarycnt);
896 
897 		l=strlen(boundarybuf);
898 
899 		for (i=0; i<argc; i++)
900 		{
901 			int	err=0;
902 			struct rfc2045 *parser=rfc2045_alloc();
903 
904 			if (!parser)
905 			{
906 				perror(argv[i]);
907 				exit(1);
908 			}
909 			if ((fp=fopen(argv[i], "r")) == 0)
910 			{
911 				perror(argv[i]);
912 				exit(1);
913 			}
914 
915 			while (fgets(mimebuf, sizeof(mimebuf), fp))
916 			{
917 				rfc2045_parse(parser, mimebuf, strlen(mimebuf));
918 
919 				if (mimebuf[0] != '-' || mimebuf[1] != '-')
920 					continue;
921 
922 				if (strncasecmp(mimebuf+2, boundarybuf, l) == 0)
923 				{
924 					err=1;
925 					break;
926 				}
927 			}
928 			fclose(fp);
929 			utf8[i]=parser->rfcviolation & RFC2045_ERR8BITHEADER
930 				? 1:0;
931 			rfc2045_free(parser);
932 			if (err)	break;
933 		}
934 	} while (i < argc);
935 
936 	printf("Mime-Version:1.0\n"
937 		"Content-Type: multipart/digest; boundary=\"%s\"\n\n%s",
938 			boundarybuf, RFC2045MIMEMSG);
939 
940 	for (i=0; i<argc; i++)
941 	{
942 		if ((fp=fopen(argv[i], "r")) == 0)
943 		{
944 			perror(argv[i]);
945 			exit(1);
946 		}
947 
948 		printf("\n--%s\nContent-Type: %s\n\n",
949 		       boundarybuf,
950 		       utf8[i] ? RFC2045_MIME_MESSAGE_GLOBAL:
951 		       RFC2045_MIME_MESSAGE_RFC822);
952 
953 		while (fgets(mimebuf, sizeof(mimebuf), fp))
954 			printf("%s", mimebuf);
955 		fclose(fp);
956 	}
957 	free(utf8);
958 	printf("\n--%s--\n", boundarybuf);
959 }
960 
display_decoded_header(const char * ptr,size_t cnt,void * dummy)961 static void display_decoded_header(const char *ptr, size_t cnt, void *dummy)
962 {
963 	if (cnt == 0)
964 		putchar('\n');
965 	else
966 		fwrite(ptr, cnt, 1, stdout);
967 }
968 
doconvtoutf8_stdout(const char * ptr,size_t n,void * dummy)969 static int doconvtoutf8_stdout(const char *ptr, size_t n, void *dummy)
970 {
971 	if (fwrite(ptr, n, 1, stdout) != 1)
972 		return -1;
973 
974 	return 0;
975 }
976 
main2(const char * mimecharset,int argc,char ** argv)977 static int main2(const char *mimecharset, int argc, char **argv)
978 {
979 int	argn;
980 char	optc;
981 char	*optarg;
982 char	*mimesection=0;
983 char	*section=0;
984 int	doinfo=0, dodecode=0, dorewrite=0, dodsn=0, domimedigest=0;
985 int	dodecodehdr=0, dodecodeaddrhdr=0, doencodemime=0, doencodemimehdr=0;
986 
987 char	*decode_header="";
988 struct	rfc2045 *p;
989 int	rwmode=0;
990 int     convtoutf8=0;
991 int	dovalidate=0;
992 void	(*do_extract)(struct rfc2045 *, const char *, int, char **)=0;
993 const char *extract_filename=0;
994 int rc=0;
995 
996 
997 	rfc2045_in_reformime=1;
998 
999 	for (argn=1; argn<argc; )
1000 	{
1001 		if (argv[argn][0] != '-')	break;
1002 		optarg=0;
1003 		optc=argv[argn][1];
1004 		if (optc && argv[argn][2])	optarg=argv[argn]+2;
1005 		++argn;
1006 		switch	(optc)	{
1007 		case 'c':
1008 			if (!optarg && argn < argc)
1009 				optarg=argv[argn++];
1010 			if (optarg && *optarg)
1011 			{
1012 				char *p=unicode_convert_tobuf("",
1013 								optarg,
1014 								unicode_u_ucs4_native,
1015 								NULL);
1016 
1017 				if (!p)
1018 				{
1019 					fprintf(stderr, "Unknown charset: %s\n",
1020 						optarg);
1021 					exit(1);
1022 				}
1023 				free(p);
1024 				mimecharset=optarg;
1025 			}
1026 			break;
1027 
1028 		case 's':
1029 			if (!optarg && argn < argc)
1030 				optarg=argv[argn++];
1031 			if (optarg && *optarg)	section=strdup(optarg);
1032 			break;
1033 		case 'i':
1034 			doinfo=1;
1035 			break;
1036 		case 'e':
1037 			dodecode=1;
1038 			break;
1039 		case 'r':
1040 			dorewrite=1;
1041 			if (optarg && *optarg == '7')
1042 				rwmode=RFC2045_RW_7BIT;
1043 			if (optarg && *optarg == '8')
1044 				rwmode=RFC2045_RW_8BIT;
1045 			if (optarg && *optarg == 'U')
1046 				rwmode=RFC2045_RW_8BIT_ALWAYS;
1047 			break;
1048 		case 'm':
1049 			domimedigest=1;
1050 			break;
1051 		case 'd':
1052 			dodsn=1;
1053 			break;
1054 		case 'D':
1055 			dodsn=2;
1056 			break;
1057 		case 'x':
1058 			do_extract=extract_file;
1059 			if (optarg)
1060 				extract_filename=optarg;
1061 			break;
1062 		case 'X':
1063 			do_extract=extract_pipe;
1064 			break;
1065 		case 'V':
1066 			dovalidate=1;
1067 			break;
1068 		case 'h':
1069 			if (!optarg && argn < argc)
1070 				optarg=argv[argn++];
1071 			if (optarg)
1072 			{
1073 				decode_header=optarg;
1074 			}
1075 			dodecodehdr=1;
1076 			break;
1077 		case 'H':
1078 			if (!optarg && argn < argc)
1079 				optarg=argv[argn++];
1080 			if (optarg)
1081 			{
1082 				decode_header=optarg;
1083 			}
1084 			dodecodeaddrhdr=1;
1085 			break;
1086 		case 'o':
1087 			if (!optarg && argn < argc)
1088 				optarg=argv[argn++];
1089 			if (optarg)
1090 			{
1091 				decode_header=optarg;
1092 			}
1093 			doencodemime=1;
1094 			break;
1095 		case 'O':
1096 			if (!optarg && argn < argc)
1097 				optarg=argv[argn++];
1098 			if (optarg)
1099 			{
1100 				decode_header=optarg;
1101 			}
1102 			doencodemimehdr=1;
1103 			break;
1104 		case 'u':
1105 			convtoutf8=1;
1106 			break;
1107 		default:
1108 			usage();
1109 		}
1110 	}
1111 
1112 	defchset=mimecharset;
1113 
1114 	rfc2045_setdefaultcharset(defchset);
1115 
1116 	if (domimedigest)
1117 	{
1118 		mimedigest(argc-argn, argv+argn);
1119 		return (0);
1120 	}
1121 	else if (dodecodehdr)
1122 	{
1123 		if (rfc822_display_hdrvalue("Subject",
1124 					    decode_header,
1125 					    mimecharset,
1126 					    display_decoded_header,
1127 					    NULL,
1128 					    NULL) < 0)
1129 		{
1130 			perror("rfc822_display_hdrvalue");
1131 			return (1);
1132 		}
1133 
1134 		printf("\n");
1135 		return (0);
1136 	}
1137 	else if (dodecodeaddrhdr)
1138 	{
1139 		if (rfc822_display_hdrvalue("To",
1140 					    decode_header,
1141 					    mimecharset,
1142 					    display_decoded_header,
1143 					    NULL,
1144 					    NULL) < 0)
1145 		{
1146 			perror("rfc822_display_hdrvalue");
1147 			return (1);
1148 		}
1149 
1150 		printf("\n");
1151 		return (0);
1152 	}
1153 
1154 	if (doencodemime)
1155 	{
1156 		char *s=rfc2047_encode_str(decode_header, mimecharset,
1157 					   rfc2047_qp_allow_any);
1158 
1159 		if (s)
1160 		{
1161 			printf("%s\n", s);
1162 			free(s);
1163 		}
1164 		return (0);
1165 	}
1166 	if (doencodemimehdr)
1167 	{
1168 		struct rfc822t *t=rfc822t_alloc_new(decode_header, NULL, NULL);
1169 		struct rfc822a *a=t ? rfc822a_alloc(t):NULL;
1170 		char *s;
1171 
1172 		if (a && (s=rfc2047_encode_header_addr(a, mimecharset)) != NULL)
1173 		{
1174 			printf("%s\n", s);
1175 			free(s);
1176 		}
1177 
1178 		if (a) rfc822a_free(a);
1179 		if (t) rfc822t_free(t);
1180 		return (0);
1181 	}
1182 
1183 	p=read_message();
1184 
1185 	if (doinfo)
1186 	{
1187 		mimesection = section ? strtok(section, ","):NULL;
1188 		do {
1189 			print_info(p, mimesection);
1190 			if (do_extract)
1191 				extract_section(p, mimesection,
1192 						extract_filename, argc-argn,
1193 						argv+argn, do_extract);
1194 			if (mimesection)
1195 				mimesection = strtok(NULL,",");
1196 		} while (mimesection != NULL);
1197 	}
1198 	else if (dodecode)
1199 	{
1200 		mimesection = section ? strtok(section,","):NULL;
1201 		do {
1202 			print_decode(p, mimesection);
1203 			if (mimesection)
1204 				mimesection = strtok(NULL,",");
1205 		} while (mimesection != NULL);
1206 	}
1207 	else if (dorewrite)
1208 		rewrite(p, rwmode);
1209 	else if (dodsn)
1210 		dsn(p, dodsn == 2);
1211 	else if (do_extract)
1212 	{
1213 		mimesection = section ? strtok(section, ","):NULL;
1214 		do {
1215 			extract_section(p, mimesection, extract_filename,
1216 					argc-argn, argv+argn, do_extract);
1217 			if (mimesection)
1218 				mimesection = strtok(NULL,",");
1219 		} while (mimesection != NULL);
1220 	}
1221 	else if (dovalidate)
1222 	{
1223 		rc=1;
1224 
1225 		if (p->rfcviolation & RFC2045_ERR2COMPLEX)
1226 			printf("ERROR: MIME complexity.\n");
1227 		else if (p->rfcviolation & RFC2045_ERRBADBOUNDARY)
1228 			printf("ERROR: Ambiguous  MIME boundary delimiters.\n");
1229 		else rc=0;
1230 
1231 	}
1232 	else if (convtoutf8)
1233 	{
1234 		struct rfc2045src *src;
1235 		struct rfc2045_decodemsgtoutf8_cb cb;
1236 
1237 		memset(&cb, 0, sizeof(cb));
1238 
1239 		cb.output_func=doconvtoutf8_stdout;
1240 		cb.arg=NULL;
1241 
1242 		src=rfc2045src_init_fd(0);
1243 
1244 		if (src)
1245 		{
1246 			rfc2045_decodemsgtoutf8(src, p, &cb);
1247 			rfc2045src_deinit(src);
1248 		}
1249 	}
1250 	else
1251 		print_structure(p);
1252 	rfc2045_free(p);
1253 	exit(rc);
1254 	return (rc);
1255 }
1256 
main(int argc,char ** argv)1257 int main(int argc, char **argv)
1258 {
1259 	int	rc;
1260 
1261 	rc=main2(unicode_default_chset(), argc, argv);
1262 	return rc;
1263 }
1264