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