/* ** Copyright 1998 - 2000 Double Precision, Inc. See COPYING for ** distribution information. */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #if HAVE_STRINGS_H #include #endif #include #include #include #include #include #include "rfc2045.h" #include "rfc822/rfc2047.h" #include "rfc2045charset.h" #if HAVE_UNISTD_H #include #endif #if HAVE_SYS_WAIT_H #include #endif //#include "numlib/numlib.h" #if HAS_GETHOSTNAME #else int gethostname(const char *, size_t); #endif static const char rcsid[]="$Id$"; void rfc2045_error(const char *errmsg) { fprintf(stderr, "reformime: %s\n", errmsg); exit(1); } static void do_print_structure(struct rfc2045 *p, struct rfc2045id *id, void *ptr) { p=p; ptr=p; while (id) { printf("%d%c", id->idnum, id->next ? '.':'\n'); id=id->next; } } static int decode_to_file(const char *p, size_t n, void *ptr) { FILE *fp=(FILE *)ptr; while (n) { --n; if (putc(*p++, fp) == -1) { perror("write"); exit(1); } } return (0); } void usage() { fprintf(stderr, "Usage: reformime [options]\n"); fprintf(stderr, " -d - parse a delivery status notification.\n"); fprintf(stderr, " -e - extract contents of MIME section.\n"); fprintf(stderr, " -x - extract MIME section to a file.\n"); fprintf(stderr, " -X - pipe MIME section to a program.\n"); fprintf(stderr, " -i - show MIME info.\n"); fprintf(stderr, " -s n.n.n.n - specify MIME section.\n"); fprintf(stderr, " -r - rewrite message, filling in missing MIME header.\n"); fprintf(stderr, " -r7 - also convert 8bit/raw encoding to quoted-printable, if possible.\n"); fprintf(stderr, " -r8 - also convert quoted-printable encoding to 8bit, if possible.\n"); fprintf(stderr, " -c charset - when rewriting, specify default charset.\n"); fprintf(stderr, " -m [file] [file]... - create a MIME message digest.\n"); exit(1); } static char *tempname(const char *tempdir) { char pidbuf[NUMBUFSIZE], timebuf[NUMBUFSIZE], hostnamebuf[256]; static unsigned counter=0; time_t t; char *p; str_pid_t(getpid(), pidbuf); time(&t); str_time_t(t, timebuf); hostnamebuf[sizeof(hostnamebuf)-1]=0; if (gethostname(hostnamebuf, sizeof(hostnamebuf))) hostnamebuf[0]=0; p=malloc(strlen(tempdir)+strlen(pidbuf)+strlen(timebuf)+ strlen(hostnamebuf)+100); if (!p) return (0); sprintf(p, "%s/%s.%s-%u.%s", tempdir, timebuf, pidbuf, counter++, hostnamebuf); return (p); } struct rfc2045 *read_message() { char buf[BUFSIZ]; struct rfc2045 *p=rfc2045_alloc_ac(); FILE *tempfp=0; if (fseek(stdin, 0L, SEEK_END) < 0 || fseek(stdin, 0L, SEEK_SET) < 0) /* Pipe, save to temp file */ { tempfp=tmpfile(); } while (fgets(buf, sizeof(buf), stdin)) { int l=strlen(buf); rfc2045_parse(p, buf, l); if (tempfp && fwrite(buf, l, 1, tempfp) != 1) { perror("fwrite"); exit(1); } } if (tempfp) /* Replace stdin */ { close(0); dup(fileno(tempfp)); fclose(tempfp); } return (p); } void print_structure(struct rfc2045 *p) { rfc2045_decode(p, &do_print_structure, 0); } static void notfound(const char *p) { fprintf(stderr, "reformime: MIME section %s not found.\n", p); exit(1); } static void do_print_info(struct rfc2045 *s) { const char *content_type, *transfer_encoding, *charset; off_t start, end, body; const char *disposition; const char *disposition_name; const char *disposition_filename; const char *content_name; off_t nlines, nbodylines; const char *p; rfc2045_mimeinfo(s, &content_type, &transfer_encoding, &charset); rfc2045_mimepos(s, &start, &end, &body, &nlines, &nbodylines); rfc2045_dispositioninfo(s, &disposition, &disposition_name, &disposition_filename); content_name=rfc2045_contentname(s); printf("content-type: %s\n", content_type); if (content_name) { char *s=rfc2047_decode_simple(content_name); if (!s) { perror("rfc2047_decode_simple"); exit(1); } printf("content-name: %s\n", s); free(s); } printf("content-transfer-encoding: %s\n", transfer_encoding); printf("charset: %s\n", charset); if (disposition && *disposition) printf("content-disposition: %s\n", disposition); if (disposition_name && *disposition_name) { char *s=rfc2047_decode_simple(disposition_name); if (!s) { perror("rfc2047_decode_simple"); exit(1); } printf("content-disposition-name: %s\n", s); free(s); } if (disposition_filename && *disposition_filename) { char *s=rfc2047_decode_simple(disposition_filename); if (!s) { perror("rfc2047_decode_simple"); exit(1); } printf("content-disposition-filename: %s\n", s); free(s); } if (*(p=rfc2045_content_id(s))) printf("content-id: <%s>\n", p); if (*(p=rfc2045_content_description(s))) { char *s=rfc2047_decode_simple(p); if (!s) { perror("rfc2047_decode_simple"); exit(1); } printf("content-description: %s\n", s); free(s); } if (*(p=rfc2045_content_language(s))) printf("content-language: %s\n", p); if (*(p=rfc2045_content_md5(s))) printf("content-md5: %s\n", p); printf("starting-pos: %lu\n", (unsigned long)start); printf("starting-pos-body: %lu\n", (unsigned long)body); printf("ending-pos: %lu\n", (unsigned long)end); printf("line-count: %lu\n", (unsigned long)nlines); printf("body-line-count: %lu\n", (unsigned long)nbodylines); } static void do_print_info_multiple(struct rfc2045 *p, struct rfc2045id *id, void *ptr) { printf("section: "); do_print_structure(p, id, ptr); do_print_info(p); printf("\n"); } void print_info(struct rfc2045 *p, const char *mimesection) { struct rfc2045 *s; if (mimesection) { s=rfc2045_find(p, mimesection); if (!s) notfound(mimesection); printf("section: %s\n", mimesection); do_print_info(s); return; } rfc2045_decode(p, &do_print_info_multiple, 0); } static void do_print_section(struct rfc2045 *s, FILE *fp) { off_t start, end, body; off_t nlines; off_t nbodylines; rfc2045_mimepos(s, &start, &end, &body, &nlines, &nbodylines); if (fseek(stdin, body, SEEK_SET) == -1) { perror("fseek"); exit(1); } rfc2045_cdecode_start(s, &decode_to_file, fp); while (body < end) { char buf[BUFSIZ]; size_t n=sizeof(buf); if ((off_t)n > end-body) n=end-body; n=fread(buf, 1, n, stdin); if (n == 0) { perror("fread"); exit(1); } rfc2045_cdecode(s, buf, n); body += n; } rfc2045_cdecode_end(s); } void print_decode(struct rfc2045 *p, const char *mimesection) { struct rfc2045 *s; if (!mimesection) usage(); s=rfc2045_find(p, mimesection); if (!s) notfound(mimesection); do_print_section(s, stdout); } void rewrite(struct rfc2045 *p, int rwmode) { rfc2045_ac_check(p, rwmode); if (rfc2045_rewrite(p, fileno(stdin), fileno(stdout), "reformime (" RFC2045PKG " " RFC2045VER ")")) { perror("reformime"); exit(1); } } static char *get_suitable_filename(struct rfc2045 *r, const char *pfix, int ignore_filename) { const char *disposition_s; const char *disposition_name_s; const char *disposition_filename_s; char *filename_buf; const char *content_name_s; char *p, *q; char *dyn_disp_name=0; rfc2045_dispositioninfo(r, &disposition_s, &disposition_name_s, &disposition_filename_s); content_name_s=rfc2045_contentname(r); if (!disposition_filename_s || !*disposition_filename_s) disposition_filename_s=disposition_name_s; if (!disposition_filename_s || !*disposition_filename_s) disposition_filename_s=content_name_s; filename_buf=rfc2047_decode_simple(disposition_filename_s); if (!filename_buf) { perror("rfc2047_decode_simple"); exit(1); } if (strlen(filename_buf) > 32) { p=filename_buf; q=filename_buf + strlen(filename_buf)-32; while ( (*p++ = *q++) != 0) ; } disposition_filename_s=filename_buf; if (ignore_filename) { char numbuf[NUMBUFSIZE]; static size_t counter=0; const char *p=str_size_t(++counter, numbuf); dyn_disp_name=malloc(strlen(disposition_filename_s) + strlen(p)+2); if (!dyn_disp_name) { perror("malloc"); exit(1); } disposition_filename_s=strcat(strcat(strcpy( dyn_disp_name, p), "-"), disposition_filename_s); } else if (!disposition_filename_s || !*disposition_filename_s) { dyn_disp_name=tempname("."); disposition_filename_s=dyn_disp_name+2; /* Skip over ./ */ } p=malloc((pfix ? strlen(pfix):0)+strlen(disposition_filename_s)+1); if (!p) { perror("malloc"); exit(1); } *p=0; if (pfix) strcpy(p, pfix); q=p+strlen(p); for (strcpy(q, disposition_filename_s); *q; q++) if (!isalnum(*q) && *q != '.' && *q != '-') *q='_'; if (dyn_disp_name) free(dyn_disp_name); if (!pfix) { const char *content_type_s; const char *content_transfer_encoding_s; const char *charset_s; int c; static char filenamebuf[256]; char *t; FILE *tty; if ((tty=fopen("/dev/tty", "r+")) == 0) { perror("/dev/tty"); exit(1); } rfc2045_mimeinfo(r, &content_type_s, &content_transfer_encoding_s, &charset_s); fprintf (tty, "Extract %s? ", content_type_s); fflush(tty); c=getc(tty); if (c != '\n' && c != EOF) { int cc; while ((cc=getc(tty)) != '\n' && cc != EOF) ; } if (c != 'y' && c != 'Y') { free(p); fclose(tty); free(filename_buf); return (0); } fprintf (tty, "Filename [%s]: ", p); fgets(filenamebuf, sizeof(filenamebuf)-1, tty); fclose(tty); t=strchr(filenamebuf, '\n'); if (t) *t=0; else { fprintf(stderr, "Filename too long.\n"); exit(1); } if (filenamebuf[0]) { free(p); p=strdup(filenamebuf); if (!p) { perror("malloc"); exit(1); } } } free(filename_buf); return (p); } static void extract_file(struct rfc2045 *p, const char *filename, int argc, char **argv) { char *f; FILE *fp; int ignore=0; for (;;) { int fd; f=get_suitable_filename(p, filename, ignore); if (!f) return; fd=open(f, O_WRONLY|O_CREAT|O_EXCL, 0666); if (fd < 0) { if (errno == EEXIST) { printf("%s exists.\n", f); free(f); ignore=1; continue; } perror(f); exit(1); } fp=fdopen(fd, "w"); if (!fp) { perror("fdopen"); exit(1); } break; } do_print_section(p, fp); if (fflush(fp) || ferror(fp)) { perror("write"); exit(1); } fclose(fp); free(f); } static void extract_pipe(struct rfc2045 *p, const char *filename, int argc, char **argv) { char *f=get_suitable_filename(p, "FILENAME=", 0); int pipefd[2]; pid_t pid, p2; FILE *fp; int waitstat; if (argc == 0) { fprintf(stderr, "reformime: Invalid -X option.\n"); exit(1); } if (pipe(pipefd)) { perror("pipe"); exit(1); } if ((fp=fdopen(pipefd[1], "w")) == 0) { perror("fdopen"); exit(1); } while ((pid=fork()) == -1) { sleep(2); } if (pid == 0) { const char *content_type_s; const char *content_transfer_encoding_s; const char *charset_s; if (!f) f="FILENAME=attachment.dat"; putenv(f); rfc2045_mimeinfo(p, &content_type_s, &content_transfer_encoding_s, &charset_s); f=malloc(strlen(content_type_s) +sizeof("CONTENT_TYPE=")); if (!f) { perror("malloc"); exit(1); } strcat(strcpy(f, "CONTENT_TYPE="), content_type_s); putenv(f); close(0); dup(pipefd[0]); close(pipefd[0]); close(pipefd[1]); execv(argv[0], argv); perror("exec"); _exit(1); } close(pipefd[0]); signal(SIGPIPE, SIG_IGN); do_print_section(p, fp); signal(SIGPIPE, SIG_DFL); fclose(fp); close(pipefd[1]); while ((p2=wait(&waitstat)) != pid && p2 != -1) ; free(f); } static void 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 **)) { if (mimesection) { top_rfcp=rfc2045_find(top_rfcp, mimesection); if (!mimesection) notfound(mimesection); if (top_rfcp->firstpart) { fprintf(stderr, "reformime: MIME section %s is a compound section.\n", mimesection); exit(1); } (*extract_func)(top_rfcp, extract_filename, argc, argv); return; } /* Recursive */ if (top_rfcp->firstpart) { for (top_rfcp=top_rfcp->firstpart; top_rfcp; top_rfcp=top_rfcp->next) extract_section(top_rfcp, mimesection, extract_filename, argc, argv, extract_func); return; } if (!top_rfcp->isdummy) (*extract_func)(top_rfcp, extract_filename, argc, argv); } static void print_dsn_recip(char *addr, char *action) { char *p, *q; if (!action || !addr) { if (action) free(action); if (addr) free(addr); return; } for (p=action; *p; ++p) *p=tolower((int)(unsigned char)*p); for (p=addr; *p && isspace((int)(unsigned char)*p); ++p) ; if (strncasecmp(p, "rfc822;", 7)) { free(action); free(addr); return; } for (q=action; *q && isspace((int)(unsigned char)*q); ++q) ; p += 7; while (*p && isspace((int)(unsigned char)*p)) ++p; printf("%s %s\n", q, p); free(action); free(addr); } static void dsn(struct rfc2045 *p, int do_orig) { const char *content_type_s; const char *content_transfer_encoding_s; const char *charset_s; off_t start_pos, end_pos, start_body; off_t dummy; const char *q; char buf[BUFSIZ]; unsigned i; int ch; char *recip; char *action; char *orecip; rfc2045_mimeinfo(p, &content_type_s, &content_transfer_encoding_s, &charset_s); if (strcasecmp(content_type_s, "multipart/report") || (q=rfc2045_getattr(p->content_type_attr, "report-type")) == 0 || strcasecmp(q, "delivery-status") || !p->firstpart || !p->firstpart->next || !p->firstpart->next->next) _exit(1); p=p->firstpart->next->next; rfc2045_mimeinfo(p, &content_type_s, &content_transfer_encoding_s, &charset_s); rfc2045_mimepos(p, &start_pos, &end_pos, &start_body, &dummy, &dummy); if (strcasecmp(content_type_s, "message/delivery-status") || fseek(stdin, start_body, SEEK_SET) == -1) _exit(1); i=0; recip=0; orecip=0; action=0; while (start_body < end_pos) { if ((ch=getchar()) == EOF) break; ++start_body; if (i < sizeof(buf)-1) buf[i++]= ch; if (ch != '\n') continue; ch=getchar(); if (ch != EOF) ungetc(ch, stdin); if (ch != '\n' && isspace((int)(unsigned char)ch)) continue; buf[i-1]=0; if (buf[0] == 0) { if (orecip) { if (recip) free(recip); recip=orecip; orecip=0; } print_dsn_recip(recip, action); recip=0; action=0; } if (strncasecmp(buf, "Final-Recipient:", 16) == 0 && recip == 0) { recip=strdup(buf+16); if (!recip) { perror("strdup"); exit(2); } } if (strncasecmp(buf, "Original-Recipient:", 19) == 0 && orecip == 0 && do_orig) { orecip=strdup(buf+19); if (!orecip) { perror("strdup"); exit(2); } } if (strncasecmp(buf, "Action:", 7) == 0 && action == 0) { action=strdup(buf+7); if (!action) { perror("strdup"); exit(2); } } i=0; } if (orecip) { if (recip) free(recip); recip=orecip; orecip=0; } print_dsn_recip(recip, action); } static void mimedigest1(int, char **); static char mimebuf[BUFSIZ]; static void mimedigest(int argc, char **argv) { char *p; struct filelist { struct filelist *next; char *fn; } *first=0, *last=0; unsigned pcnt=0; char **l; if (argc > 0) { mimedigest1(argc, argv); return; } while (fgets(mimebuf, sizeof(mimebuf), stdin)) { struct filelist *q; if ((p=strchr(mimebuf, '\n')) != 0) *p=0; q=malloc(sizeof(struct filelist)); if (!q || !(q->fn=strdup(mimebuf))) { perror("malloc"); exit(1); } if (last) last->next=q; else first=q; last=q; q->next=0; ++pcnt; } if (pcnt == 0) return; if ( (l=malloc(sizeof (char *) * pcnt)) == 0) { perror("malloc"); } pcnt=0; for (last=first; last; last=last->next) l[pcnt++]=last->fn; mimedigest1(pcnt, l); } static void mimedigest1(int argc, char **argv) { time_t t; char boundarybuf[200]; unsigned boundarycnt=0; int i; FILE *fp; time (&t); /* Search for a suitable boundary */ do { int l; sprintf(boundarybuf, "reformime_%lu_%u", (unsigned long)t, ++boundarycnt); l=strlen(boundarybuf); for (i=0; i