#include #include #include #include #include extern const char *__progname; #define UNUSED_ARG(x) x __attribute__((__unused__)) typedef struct strr STRR; struct strr { const char *buf; int ptr; int len; } ; typedef struct attrval ATTRVAL; struct attrval { ATTRVAL *link; char *name; char *value; } ; typedef struct values VALUES; struct values { ATTRVAL *vals; } ; typedef struct multipart_info MULTIPART_INFO; struct multipart_info { char *boundary; int boundary_len; int (*enc_termfxn)(char *, void *); void *enc_termtok; unsigned int flags; #define MIF_LASTBSEEN 0x00000001 } ; typedef struct linestack LINESTACK; struct linestack { LINESTACK *link; char *line; } ; static VALUES msghdr; static LINESTACK *pushed_lines; static FILE *inputfile; /* Use these instead of is*(), to avoid "subscript has type `char'" warnings on compilers that detect that. */ #define ISSPACE(x) isspace((unsigned char)(x)) #define ISUPPER(x) isupper((unsigned char)(x)) #define TOLOWER(x) tolower((unsigned char)(x)) static char *get_1521_token(FILE *f) { int bq; int dq; int cl; int c; char *s; int a; int l; bq = 0; dq = 0; cl = 0; a = 15; l = 0; s = malloc(a+1); while (1) { c = getc(f); if (c == EOF) { if (l == 0) { free(s); return(0); } s[l] = '\0'; return(s); } if (cl) { if (bq) { bq = 0; } else { switch (c) { case '(': cl ++; break; case ')': cl --; break; case '\\': bq = 1; break; } } continue; } else if (bq) { bq = 0; } else if (dq) { switch (c) { case '"': s[l] = '\0'; return(s); break; case '\\': bq = 1; continue; break; /* By my reading of 1521, we should also ignore whitespace here, but that turns out to be the Wrong Thing in practice. Too many people put spaces in MIME boundary= strings and expect them to "work". case ' ': case '\t': case '\r': case '\n': continue; break; */ } } else { switch (c) { case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '!': case '#': case '$': case '%': case '&': case '*': case '+': case '-': case '.': case '^': case '_': case '`': case '|': case '~': case '{': case '}': case '\'': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': /* This list is based on tspecials in 1521, not the lists in 822 (which would, among other things, exclude . and include / and =, which is Not Right for our purposes.) */ break; case '"': if (l == 0) { dq = 1; continue; } else { ungetc(c,f); s[l] = '\0'; return(s); } break; case ' ': case '\t': case '\r': case '\n': if (l == 0) continue; s[l] = '\0'; return(s); break; case '(': if (l == 0) { cl = 1; continue; } else { ungetc(c,f); s[l] = '\0'; return(s); } break; default: if (l == 0) { s[0] = c; s[1] = '\0'; return(s); } else { ungetc(c,f); s[l] = '\0'; return(s); } break; } } if (l >= a) s = realloc(s,(a=l+15)+1); s[l++] = c; } } static char *getline(void) { char *buf; int len; int have; int c; if (pushed_lines) { LINESTACK *ls; ls = pushed_lines; pushed_lines = ls->link; buf = ls->line; free(ls); #ifdef DEBUG_ECHO_INPUT printf("[popping pushed line: %s]\n",buf); #endif return(buf); } buf = malloc(1); len = 0; have = 0; while (1) { c = getc(inputfile); if (c == EOF) { if (len > 0) { fprintf(stderr,"%s: warning: missing newline supplied at EOF\n",__progname); buf[len] = '\0'; #ifdef DEBUG_ECHO_INPUT printf("[read line: %s]\n",buf); #endif return(buf); } free(buf); return(0); } if (c == '\n') { if ((len > 0) && (buf[len-1] == '\r')) len --; buf[len] = '\0'; #ifdef DEBUG_ECHO_INPUT printf("[read line: %s]\n",buf); #endif return(buf); } if (len >= have) buf = realloc(buf,(have=len+8)+1); buf[len++] = c; } } static void put_line_back(char *line) { LINESTACK *ls; ls = malloc(sizeof(LINESTACK)); ls->line = line; ls->link = pushed_lines; pushed_lines = ls; #ifdef DEBUG_ECHO_INPUT printf("[pushed line: %s]\n",line); #endif } static void readheader(VALUES *h) { char *line; ATTRVAL *v; ATTRVAL **tail; tail = &h->vals; while (1) { line = getline(); if (! line) break; if (! line[0]) { free(line); break; } v = malloc(sizeof(ATTRVAL)); v->value = line; *tail = v; tail = &v->link; } *tail = 0; tail = &h->vals; while ((v=*tail)) { if (v->link && ISSPACE(v->link->value[0])) { char *cp; ATTRVAL *vt; for (cp=v->link->value;*cp&&ISSPACE(*cp);cp++) ; if (*cp) { char *t; t = malloc(strlen(v->value)+1+strlen(cp)+1); sprintf(t,"%s %s",v->value,cp); free(v->value); v->value = t; } free(v->link->value); vt = v->link->link; free(v->link); v->link = vt; } else { char *cp; char *vt; vt = v->value; cp = index(vt,':'); if (! cp) { *tail = v->link; free(v->value); free(v); } else { int l; l = cp - vt; v->name = malloc(l+1); bcopy(vt,v->name,l); v->name[l] = '\0'; for (cp++;*cp&&ISSPACE(*cp);cp++) ; l = strlen(cp); v->value = malloc(l+1); strcpy(v->value,cp); free(vt); tail = &v->link; } } } } static char *getval(VALUES *h, const char *name) { ATTRVAL *v; for (v=h->vals;v;v=v->link) { if (!strcasecmp(v->name,name)) return(v->value); } return(0); } static int tspecial_char(char c) { switch (c) { case '(': case ')': case '<': case '>': case '@': case ',': case ';': case ':': case '\\': case '"': case '/': case '[': case ']': case '?': case '=': return(1); } return(0); } static char *skiptoken(const char **sp) { const char *s; const char *s0; char *rv; s = *sp; s0 = s; while (*s && !ISSPACE(*s) && !tspecial_char(*s)) s ++; rv = malloc((s-s0)+1); bcopy(s0,rv,s-s0); rv[s-s0] = '\0'; *sp = s; return(rv); } static char *skipqsval(const char **sp) { const char *s; char *rv; int len; int have; s = *sp; if (*s != '"') return(skiptoken(sp)); rv = malloc(1); len = 0; have = 0; s ++; while (1) { switch (*s) { case '\0': free(rv); fprintf(stderr,"%s: warning: end of line inside quoted string\n",__progname); return(0); break; case '"': rv[len] = '\0'; *sp = s; return(rv); break; case '\\': s ++; if (*s == '\0') free(rv); fprintf(stderr,"%s: warning: end of line after \\ inside quoted string\n",__progname); return(0); break; } if (len >= have) rv = realloc(rv,(have=len+8)+1); rv[len++] = *s++; } } static void get_parameters(const char *s, VALUES *vs) { char *n; char *v; ATTRVAL *av; vs->vals = 0; while (1) { s = index(s,';'); if (s == 0) break; s ++; while (*s && ISSPACE(*s)) s ++; n = skiptoken(&s); while (*s && ISSPACE(*s)) s ++; if (*s != '=') { free(n); break; } s ++; while (*s && ISSPACE(*s)) s ++; v = skipqsval(&s); if (! v) { free(n); break; } av = malloc(sizeof(ATTRVAL)); av->name = n; av->value = v; av->link = vs->vals; vs->vals = av; } } static void free_values(VALUES *v) { ATTRVAL *av; while (v->vals) { av = v->vals; v->vals = av->link; free(av->name); free(av->value); free(av); } } static int skip_to_boundary(int (*term)(char *, void *), void *termtoken, int consumeit) { char *l; while (1) { l = getline(); if (! l) return(1); if ((*term)(l,termtoken)) { if (consumeit) free(l); else put_line_back(l); return(0); } free(l); } } static int multipart_term(char *line, void *token) { MULTIPART_INFO *mi; int l; mi = token; if ((*mi->enc_termfxn)(line,mi->enc_termtok)) { put_line_back(line); return(1); } if ((line[0] != '-') || (line[1] != '-')) return(0); l = strlen(line+2); if ((l == mi->boundary_len) && !bcmp(mi->boundary,line+2,l)) return(1); if ( (l == mi->boundary_len+2) && (line[l] == '-') && (line[l+1] == '-') && !bcmp(mi->boundary,line+2,l-2) ) { mi->flags |= MIF_LASTBSEEN; return(1); } return(0); } static void fgets_strip(char *s) { int l; l = strlen(s); if ((l > 0) && (s[l-1] == '\n')) s[l-1] = '\0'; } typedef struct base64_state BASE64_STATE; struct base64_state { unsigned int n; unsigned int acc; unsigned int flags; #define BASE64F_ENDED 0x00000001 #define BASE64F_WARNED 0x00000002 } ; static char base64_encoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static void *base64_init(void) { BASE64_STATE *s; s = malloc(sizeof(BASE64_STATE)); s->n = 0; s->flags = 0; return(s); } static void base64_line(char *line, void *state, void (*data)(const void *, int, void *), void *datatoken) { BASE64_STATE *s; char c; char buf[3]; char *e; s = state; for (;(c=*line);line++) { if (c == '=') { switch (s->n) { case 0: break; case 1: fprintf(stderr,"%s: warning: base64: = at invalid place\n",__progname); break; case 2: buf[0] = (s->acc >> 4) & 0xff; (*data)(&buf[0],1,datatoken); break; case 3: buf[0] = (s->acc >> 10) & 0xff; buf[1] = (s->acc >> 2) & 0xff; (*data)(&buf[0],2,datatoken); break; } s->flags |= BASE64F_ENDED; } else { e = index(&base64_encoding[0],c); if (e) { if (s->flags & BASE64F_ENDED) { if (! (s->flags & BASE64F_WARNED)) { fprintf(stderr,"%s: base64 data after endmarker ignored\n",__progname); s->flags |= BASE64F_WARNED; } } else { s->acc = (s->acc << 6) | (e - &base64_encoding[0]); s->n ++; if (s->n > 3) { buf[0] = (s->acc >> 16) & 0xff; buf[1] = (s->acc >> 8) & 0xff; buf[2] = (s->acc ) & 0xff; (*data)(&buf[0],3,datatoken); s->n = 0; } } } } } } static void base64_finish(void *state, UNUSED_ARG(void (*data)(const void *, int, void *)), UNUSED_ARG(void *datatoken)) { BASE64_STATE *s; s = state; if (! (s->flags & BASE64F_ENDED)) { if (s->n != 0) { fprintf(stderr,"%s: base64: premature end of data\n",__progname); } } free(s); } static const char xdig[] = "0123456789ABCDEF"; typedef struct q_p_state Q_P_STATE; struct q_p_state { int eqs; #define QP_EQS_NONE 1 #define QP_EQS_EQUAL 2 #define QP_EQS_MIDHEX 3 char *spcbuf; int spclen; int spcalloc; int spcptr; unsigned char xbuf; } ; static void *quoted_printable_init(void) { Q_P_STATE *s; s = malloc(sizeof(Q_P_STATE)); s->eqs = QP_EQS_NONE; s->spcbuf = 0; s->spclen = 0; s->spcalloc = 0; s->spcptr = 0; return(s); } static void quoted_printable_line(char *line, void *state, void (*data)(const void *, int, void *), void *datatoken) { char c; Q_P_STATE *s; char *xp; void engine(char c) { switch (s->eqs) { case QP_EQS_NONE: if (c == '=') { s->eqs = QP_EQS_EQUAL; } else { (*data)(&c,1,datatoken); } break; case QP_EQS_EQUAL: if (c == '\n') { s->eqs = QP_EQS_NONE; } else { xp = index(&xdig[0],c); if (xp) { s->eqs = QP_EQS_MIDHEX; s->xbuf = (xp-&xdig[0]) << 4; } else { fprintf(stderr,"%s: invalid character 0x%02x after =\n",__progname,0xff&(int)c); s->eqs = QP_EQS_NONE; } } break; case QP_EQS_MIDHEX: s->eqs = QP_EQS_NONE; xp = index(&xdig[0],c); if (xp) { c = s->xbuf | (xp-&xdig[0]); (*data)(&c,1,datatoken); } else { fprintf(stderr,"%s: invalid second character 0x%02x in hex = encoding\n",__progname,0xff&(int)c); } break; } } s = state; while (1) { if (s->spcptr > 0) { c = s->spcbuf[s->spcptr++]; if (s->spcptr >= s->spclen) { s->spcptr = 0; s->spclen = 0; } } else { c = *line++; if (! c) { s->spclen = 0; engine('\n'); break; } switch (c) { case ' ': case '\t': if (s->spclen >= s->spcalloc) s->spcbuf = realloc(s->spcbuf,(s->spcalloc=s->spclen+8)+1); s->spcbuf[s->spclen++] = c; continue; break; } if (s->spclen > 0) { s->spcbuf[s->spclen++] = c; s->spcptr = 1; c = s->spcbuf[0]; } } engine(c); } } static void quoted_printable_finish(void *state, UNUSED_ARG(void (*data)(const void *, int, void *)), UNUSED_ARG(void *datatoken)) { Q_P_STATE *s; s = state; switch (s->eqs) { case QP_EQS_NONE: break; default: fprintf(stderr,"%s: end of part partway through hex = encoding\n",__progname); break; } free(s->spcbuf); free(s); } static void *nocoding_init(void) { return(0); } static void nocoding_line(char *line, UNUSED_ARG(void *state), void (*data)(const void *, int, void *), void *datatoken) { (*data)(line,strlen(line),datatoken); (*data)("\n",1,datatoken); } static void nocoding_finish(UNUSED_ARG(void *state), UNUSED_ARG(void (*data)(const void *, int, void *)), UNUSED_ARG(void *datatoken)) { } static void decode_body(VALUES *h, void (*data)(const void *, int, void *), void *datatoken, int (*term)(char *, void *), void *termtoken) { const char *cte; char *l; void *state; void (*processline)(char *, void *, void (*)(const void *, int, void *), void *); void (*finish)(void *, void (*)(const void *, int, void *), void *); cte = getval(h,"content-transfer-encoding"); if (cte == 0) cte = "7bit"; if (!strcasecmp(cte,"base64")) { state = base64_init(); processline = base64_line; finish = base64_finish; } else if (!strcasecmp(cte,"quoted-printable")) { state = quoted_printable_init(); processline = quoted_printable_line; finish = quoted_printable_finish; } else if ( !strcasecmp(cte,"7bit") || !strcasecmp(cte,"8bit") || !strcasecmp(cte,"binary") ) { state = nocoding_init(); processline = nocoding_line; finish = nocoding_finish; } else { fprintf(stderr,"%s: unknown Content-Transfer-Encoding %s\n",__progname,cte); skip_to_boundary(term,termtoken,0); return; } while (1) { l = getline(); if (! l) break; if ((*term)(l,termtoken)) break; (*processline)(l,state,data,datatoken); free(l); } put_line_back(l); (*finish)(state,data,datatoken); } static void text_to_stdio(const void *data, int len, void *fvp) { fwrite(data,len,1,fvp); } static void process_body(VALUES *, int (*)(char *, void *), void *); /* fwd */ static void do_multipart_body(char *subtype, VALUES *params, int (*term)(char *, void *), void *termtoken) { MULTIPART_INFO mi; VALUES parthdr; printf("Multipart (%s)\n",subtype); mi.boundary = getval(params,"boundary"); if (! mi.boundary) { fprintf(stderr,"%s: no boundary given for multipart body!\n",__progname); } else { mi.boundary_len = strlen(mi.boundary); mi.enc_termfxn = term; mi.enc_termtok = termtoken; mi.flags = 0; while (1) { if (skip_to_boundary(multipart_term,&mi,1)) { fprintf(stderr,"%s: no closing boundary for multipart body!\n",__progname); break; } if (mi.flags & MIF_LASTBSEEN) break; readheader(&parthdr); process_body(&parthdr,multipart_term,&mi); free_values(&parthdr); } } skip_to_boundary(term,termtoken,0); } static void do_image_body(char *subtype, VALUES *params, VALUES *hdr, int (*term)(char *, void *), void *termtoken) { char *v; char *name; char saveto[1024]; char *savename; char *lversion; char *cdisp; int i; VALUES pcd; printf("Image (%s)\n",subtype); v = getval(hdr,"content-description"); if (v) printf("Content-Description: %s\n",v); cdisp = getval(hdr,"content-disposition"); if (cdisp) printf("Content-Disposition: %s\n",cdisp); get_parameters(cdisp?cdisp:"",&pcd); name = getval(params,"name"); if (name == 0) name = getval(params,"filename"); if (name == 0) name = getval(&pcd,"name"); if (name == 0) name = getval(&pcd,"filename"); lversion = 0; if (name) { for (i=0;name[i];i++) if (ISUPPER(name[i])) break; if (name[i]) { lversion = malloc(strlen(name)+1); for (i=0;name[i];i++) lversion[i] = TOLOWER(name[i]); lversion[i] = 0; } } getfilename:; if (name) { printf("Enter save name or - to not save%s%s [%s]: ",lversion?" or l for ":"",lversion?:"",name); if (fgets(&saveto[0],sizeof(saveto),stdin) != &saveto[0]) { strcpy(&saveto[0],"-"); } else { fgets_strip(&saveto[0]); } savename = (saveto[0] == '\0') ? name : (lversion && (saveto[0] == 'l') && (saveto[1] == '\0')) ? lversion : ((saveto[0] == '-') && (saveto[1] == '\0')) ? 0 : &saveto[0]; } else { printf("Enter save name, or - or nothing to not save: "); if (fgets(&saveto[0],sizeof(saveto),stdin) != &saveto[0]) { saveto[0] = '\0'; } else { fgets_strip(&saveto[0]); } savename = (saveto[0] == '\0') ? 0 : ((saveto[0] == '-') && (saveto[1] == '\0')) ? 0 : &saveto[0]; } if (savename) { FILE *f; f = fopen(savename,"w"); if (f == 0) { fprintf(stderr,"%s: can't open %s: %s\n",__progname,savename,strerror(errno)); goto getfilename; } decode_body(hdr,text_to_stdio,f,term,termtoken); fclose(f); } if (lversion) free(lversion); free_values(&pcd); } static void tmpfile_to(FILE *from, FILE *to) { int c; rewind(from); while (1) { c = getc(from); if (c == EOF) break; putc(c,to); } } static void do_text_body(const char *subtype, VALUES *h, int (*term)(char *, void *), void *termtoken) { char *v; char saveto[1024]; FILE *txtf; txtf = tmpfile(); decode_body(h,text_to_stdio,txtf,term,termtoken); if (0) { reprompt:; printf("(Repeat) "); } printf("Text (%s)\n",subtype); v = getval(h,"content-description"); if (v) printf("Content-Description: %s\n",v); v = getval(h,"content-disposition"); if (v) printf("Content-Disposition: %s\n",v); printf("Enter save name, - for stdout, \"p\" for $PAGER, nothing to toss: "); if (fgets(&saveto[0],sizeof(saveto),stdin) != &saveto[0]) { strcpy(&saveto[0],""); } else { fgets_strip(&saveto[0]); } if (!strcmp(&saveto[0],"")) { } else if (!strcmp(&saveto[0],"-")) { tmpfile_to(txtf,stdout); goto reprompt; } else if (!strcmp(&saveto[0],"p")) { FILE *f; f = popen("exec $PAGER","w"); if (f == 0) { fprintf(stderr,"%s: can't popen $PAGER: %s\n",__progname,strerror(errno)); goto reprompt; } tmpfile_to(txtf,f); pclose(f); goto reprompt; } else { FILE *f; f = fopen(&saveto[0],"w"); if (f == 0) { fprintf(stderr,"%s: can't open %s: %s\n",__progname,&saveto[0],strerror(errno)); goto reprompt; } tmpfile_to(txtf,f); fclose(f); goto reprompt; } fclose(txtf); } static void do_message_body(const char *ctype, char *subtype, VALUES *h, int (*term)(char *, void *), void *termtoken) { VALUES h2; printf("Message (%s)\n",subtype); if (!strcasecmp(subtype,"rfc822")) { readheader(&h2); process_body(&h2,term,termtoken); free_values(&h2); } else { printf("Content-Type: message/%s; %s\n",subtype,ctype); do_text_body("really message",h,term,termtoken); } } static void do_application_body(const char *ctype, const char *subtype, VALUES *hdr, VALUES *params, int (*term)(char *, void *), void *termtoken) { char *v; char *name; char saveto[1024]; char *savename; printf("application/%s%s\n",subtype,ctype); v = getval(hdr,"content-description"); if (v) printf("Content-Description: %s\n",v); v = getval(hdr,"content-disposition"); if (v) printf("Content-Disposition: %s\n",v); name = getval(params,"name"); if (! name) name = getval(params,"filename"); getfilename:; if (name) { printf("Enter save name or - to not save [%s]: ",name); if (fgets(&saveto[0],sizeof(saveto),stdin) != &saveto[0]) { strcpy(&saveto[0],"-"); } else { fgets_strip(&saveto[0]); } savename = (saveto[0] == '\0') ? name : ((saveto[0] == '-') && (saveto[1] == '\0')) ? 0 : &saveto[0]; } else { printf("Enter save name, or - or nothing to not save: "); if (fgets(&saveto[0],sizeof(saveto),stdin) != &saveto[0]) { saveto[0] = '\0'; } else { fgets_strip(&saveto[0]); } savename = (saveto[0] == '\0') ? 0 : ((saveto[0] == '-') && (saveto[1] == '\0')) ? 0 : &saveto[0]; } if (savename) { FILE *f; f = fopen(savename,"w"); if (f == 0) { fprintf(stderr,"%s: can't open %s: %s\n",__progname,savename,strerror(errno)); goto getfilename; } decode_body(hdr,text_to_stdio,f,term,termtoken); fclose(f); } } static void process_body(VALUES *h, int (*term)(char *, void *), void *termtoken) { const char *ctype; char *type; char *subtype; VALUES params; ctype = getval(h,"content-type"); if (ctype == 0) ctype = "text/plain; charset=us-ascii"; type = skiptoken(&ctype); while (*ctype && ISSPACE(*ctype)) ctype ++; if (*ctype != '/') { fprintf(stderr,"%s: no slash in Content-Type!\n",__progname); free(type); skip_to_boundary(term,termtoken,0); return; } ctype ++; while (*ctype && ISSPACE(*ctype)) ctype ++; subtype = skiptoken(&ctype); get_parameters(ctype,¶ms); if (!strcasecmp(type,"multipart")) { do_multipart_body(subtype,¶ms,term,termtoken); } else if (!strcasecmp(type,"image")) { do_image_body(subtype,¶ms,h,term,termtoken); } else if (!strcasecmp(type,"text")) { do_text_body(subtype,h,term,termtoken); } else if (!strcasecmp(type,"message")) { do_message_body(ctype,subtype,h,term,termtoken); } else if (!strcasecmp(type,"application")) { do_application_body(ctype,subtype,h,¶ms,term,termtoken); } else { printf("Unknown Content-Type: %s/%s%s\n",type,subtype,ctype); printf("Treating as application/octet-stream.\n"); do_application_body("","octet-stream",h,¶ms,term,termtoken); } free_values(¶ms); free(type); free(subtype); } static int no_boundary(UNUSED_ARG(char *line), UNUSED_ARG(void *token)) { return(0); } static int strr_r(void *pv, char *buf, int len) { STRR *p; p = pv; if (p->ptr < 0) return(EINVAL); if (p->ptr >= p->len) return(0); if (len > p->len-p->ptr) len = p->len - p->ptr; bcopy(p->buf+p->ptr,buf,len); p->ptr += len; return(len); } static fpos_t strr_s(void *pv, fpos_t to, int whence) { STRR *p; int ito; int newptr; p = pv; ito = to; if (ito != to) return(EINVAL); switch (whence) { case SEEK_SET: newptr = ito; break; case SEEK_CUR: newptr += p->ptr + ito; break; case SEEK_END: newptr = p->len + ito; break; default: return(EINVAL); break; } if (newptr < 0) return(EINVAL); p->ptr = newptr; return(0); } static int strr_c(void *pv) { free(pv); return(0); } static FILE *fopenstrr(const char *s) { STRR *p; FILE *f; int e; p = malloc(sizeof(STRR)); if (p == 0) return(0); p->buf = s; p->ptr = 0; p->len = strlen(s); f = funopen(p,&strr_r,0,&strr_s,&strr_c); if (f == 0) { e = errno; free(p); errno = e; return(0); } return(f); } int main(int, char **); int main(int ac, char **av) { char *mimever; FILE *f; char *ver; for (ac--,av++;ac;ac--,av++) { inputfile = fopen(*av,"r"); if (inputfile == 0) { fprintf(stderr,"%s: can't open %s: %s\n",__progname,*av,strerror(errno)); continue; } printf("---Input file: %s\n",*av); readheader(&msghdr); mimever = getval(&msghdr,"mime-version"); if (mimever == 0) { fprintf(stderr,"%s: no Mime-Version: header\n",__progname); exit(1); } f = fopenstrr(mimever); ver = get_1521_token(f); if (strcmp(ver,"1.0")) { fprintf(stderr,"%s: Mime-Version: is not 1.0\n",__progname); exit(1); } free(ver); ver = get_1521_token(f); if (ver) { fprintf(stderr,"%s: Mime-Version: has tokens after 1.0\n",__progname); exit(1); } fclose(f); process_body(&msghdr,no_boundary,0); skip_to_boundary(no_boundary,0,1); fclose(inputfile); free_values(&msghdr); } exit(0); }