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