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