This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
yap-6.3/packages/clib/maildrop/rfc2045/reformime.c
Vítor Santos Costa 40febfdf9b clib package
2010-06-17 00:40:25 +01:00

963 lines
18 KiB
C

/*
** Copyright 1998 - 2000 Double Precision, Inc. See COPYING for
** distribution information.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#if HAVE_STRINGS_H
#include <strings.h>
#endif
#include <stdlib.h>
#include <ctype.h>
#include <pwd.h>
#include <fcntl.h>
#include <signal.h>
#include "rfc2045.h"
#include "rfc822/rfc2047.h"
#include "rfc2045charset.h"
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_SYS_WAIT_H
#include <sys/wait.h>
#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<argc; i++)
{
int err=0;
if ((fp=fopen(argv[i], "r")) == 0)
{
perror(argv[i]);
exit(1);
}
while (fgets(mimebuf, sizeof(mimebuf), fp))
{
if (mimebuf[0] != '-' || mimebuf[1] != '-')
continue;
if (strncasecmp(mimebuf+2, boundarybuf, l) == 0)
{
err=1;
break;
}
}
fclose(fp);
if (err) break;
}
} while (i < argc);
printf("Mime-Version:1.0\n"
"Content-Type: multipart/digest; boundary=\"%s\"\n\n%s",
boundarybuf, RFC2045MIMEMSG);
for (i=0; i<argc; i++)
{
if ((fp=fopen(argv[i], "r")) == 0)
{
perror(argv[i]);
exit(1);
}
printf("\n--%s\nContent-Type: message/rfc822\n\n",
boundarybuf);
while (fgets(mimebuf, sizeof(mimebuf), fp))
printf("%s", mimebuf);
fclose(fp);
}
printf("\n--%s--\n", boundarybuf);
}
int main(int argc, char **argv)
{
int argn;
char optc;
char *optarg;
char *mimesection=0;
int doinfo=0, dodecode=0, dorewrite=0, dodsn=0, domimedigest=0;
struct rfc2045 *p;
int rwmode=0;
void (*do_extract)(struct rfc2045 *, const char *, int, char **)=0;
const char *extract_filename=0;
for (argn=1; argn<argc; )
{
if (argv[argn][0] != '-') break;
optarg=0;
optc=argv[argn][1];
if (optc && argv[argn][2]) optarg=argv[argn]+2;
++argn;
switch (optc) {
case 'c':
if (!optarg && argn < argc)
optarg=argv[argn++];
if (optarg && *optarg)
rfc2045_setdefaultcharset(optarg);
break;
case 's':
if (!optarg && argn < argc)
optarg=argv[argn++];
if (optarg && *optarg) mimesection=optarg;
break;
case 'i':
doinfo=1;
break;
case 'e':
dodecode=1;
break;
case 'r':
dorewrite=1;
if (optarg && *optarg == '7')
rwmode=RFC2045_RW_7BIT;
if (optarg && *optarg == '8')
rwmode=RFC2045_RW_8BIT;
break;
case 'm':
domimedigest=1;
break;
case 'd':
dodsn=1;
break;
case 'D':
dodsn=2;
break;
case 'x':
do_extract=extract_file;
if (optarg)
extract_filename=optarg;
break;
case 'X':
do_extract=extract_pipe;
break;
case 'v':
printf("%s\n", rcsid);
exit(0);
default:
usage();
}
}
if (domimedigest)
{
mimedigest(argc-argn, argv+argn);
return (0);
}
p=read_message();
if (doinfo)
print_info(p, mimesection);
else if (dodecode)
print_decode(p, mimesection);
else if (dorewrite)
rewrite(p, rwmode);
else if (dodsn)
dsn(p, dodsn == 2);
else if (do_extract)
extract_section(p, mimesection, extract_filename,
argc-argn, argv+argn, do_extract);
else
print_structure(p);
rfc2045_free(p);
exit(0);
return (0);
}