static void content_id(struct rfc2045 *p, struct rfc822t *t) { struct rfc822a *a=rfc822a_alloc(t); int i; if (!a) { rfc2045_enomem(); return; } for (i=0; i<a->naddrs; i++) if (a->addrs[i].tokens) { char *s=rfc822_getaddr(a, i); if (!s) { rfc822a_free(a); rfc2045_enomem(); return; } if (p->content_id) free(p->content_id); p->content_id=s; break; } rfc822a_free(a); }
void rfc2045_setattr(struct rfc2045attr **p, const char *name, const char *val) { char *v; while (*p) { if (strcmp( (*p)->name, name) == 0) break; p=&(*p)->next; } if (val == 0) { struct rfc2045attr *q= *p; if (q) { *p=q->next; if (q->name) free(q->name); if (q->value) free(q->value); free(q); } return; } v=strdup(val); if (!v) { rfc2045_enomem(); return; } if (!*p) { if (((*p)=(struct rfc2045attr *)malloc(sizeof(**p))) == 0) { free(v); rfc2045_enomem(); return; } memset( (*p), 0, sizeof(**p)); if ( ((*p)->name=strdup(name)) == 0) { free( *p ); *p=0; free(v); rfc2045_enomem(); return; } } if ( (*p)->value ) free ( (*p)->value ); (*p)->value=v; }
void rfc2045_add_buf( char **bufptr, /* Buffer */ size_t *bufsize, /* Buffer's maximum size */ size_t *buflen, /* Buffer's current size */ const char *p, size_t len) /* Append this data */ { if (len + *buflen > *bufsize) { size_t newsize=len+*buflen+256; char *p= *bufptr ? (char *)realloc(*bufptr, newsize): (char *)malloc(newsize); if (!p) { rfc2045_enomem(); return; } *bufptr=p; *bufsize=newsize; } memcpy(*bufptr + *buflen, p, len); *buflen += len; }
void rfc2045_setdefaultcharset(const char *charset) { char *p=strdup(charset); if (!p) { rfc2045_enomem(); return; } if (rfc2045_defcharset) free(rfc2045_defcharset); rfc2045_defcharset=p; }
char *rfc2045_related_start(const struct rfc2045 *p) { const char *cb=rfc2045_getattr( p->content_type_attr, "start"); struct rfc822t *t; struct rfc822a *a; int i; if (!cb || !*cb) return (0); t=rfc822t_alloc_new(cb, 0, NULL); if (!t) { rfc2045_enomem(); return(0); } a=rfc822a_alloc(t); if (!a) { rfc822t_free(t); rfc2045_enomem(); return (0); } for (i=0; i<a->naddrs; i++) if (a->addrs[i].tokens) { char *s=rfc822_getaddr(a, i); rfc822a_free(a); rfc822t_free(t); if (!s) rfc2045_enomem(); return (s); } rfc822a_free(a); rfc822t_free(t); return (0); }
char *rfc2045_mk_boundary(struct rfc2045 *s, int fd) { char pidbuf[NUMBUFSIZE]; char timebuf[NUMBUFSIZE]; char cntbuf[60]; int cnt=0; time_t mytime; #ifndef __WINDOWS__ char hostnamebuf[256]; pid_t mypid; #endif char *p; int rc; time(&mytime); #ifdef __WINDOWS__ sprintf(pidbuf, "%ld", GetCurrentThreadId()); sprintf(timebuf, "%ld", (long)mytime); #else hostnamebuf[sizeof(hostnamebuf)-1]=0; if (gethostname(hostnamebuf, sizeof(hostnamebuf)-1)) hostnamebuf[0]=0; mypid=getpid(); sprintf(pidbuf, "%d", (int)mypid); sprintf(timebuf, "%ld", mytime); #endif for (;;) { sprintf(cntbuf, "%d", ++cnt); p=malloc(strlen(pidbuf)+strlen(timebuf)+ strlen(cntbuf)+10); if (!p) { rfc2045_enomem(); return (NULL); } sprintf(p, "=_%s-%s-%s", pidbuf, timebuf, cntbuf); if ((rc=rfc2045_try_boundary(s, fd, p)) == 0) break; free(p); if (rc < 0) return (NULL); } return (p); }
static void set_string(char **p, const char *q) { if (*p) free(*p); *p=0; if (!q) return; if ((*p=(char *)malloc(strlen(q)+1)) == 0) { rfc2045_enomem(); return; } strcpy(*p, q); }
static char *paste_tokens(struct rfc822t *h, int start, int cnt) { int l; int i; char *p; /* Calculate string size */ l=1; for (i=0; i<cnt; i++) { if (h->tokens[start+i].token == '(') continue; if (rfc822_is_atom(h->tokens[start+i].token)) l += h->tokens[start+i].len; else l++; } /* Do it */ p=( char *)malloc(l); if (!p) { rfc2045_enomem(); return (0); } l=0; for (i=0; i<cnt; i++) { if (h->tokens[start+i].token == '(') continue; if (rfc822_is_atom(h->tokens[start+i].token)) { int l2=h->tokens[start+i].len; memcpy(p+l, h->tokens[start+i].ptr, l2); l += l2; } else p[l++]=h->tokens[start+i].token; } p[l]=0; return (p); }
char *rfc2045_mk_boundary(struct rfc2045 *s, int fd) { char hostnamebuf[256]; pid_t mypid; char pidbuf[NUMBUFSIZE]; time_t mytime; char timebuf[NUMBUFSIZE]; static size_t cnt=0; char cntbuf[NUMBUFSIZE]; char *p; int rc; hostnamebuf[sizeof(hostnamebuf)-1]=0; if (gethostname(hostnamebuf, sizeof(hostnamebuf)-1)) hostnamebuf[0]=0; mypid=getpid(); time(&mytime); libmail_str_pid_t(mypid, pidbuf); libmail_str_time_t(mytime, timebuf); for (;;) { char tempbuf[NUMBUFSIZE]; libmail_str_size_t(++cnt, tempbuf); sprintf(cntbuf, "%4s", tempbuf); for (p=cntbuf; *p == ' '; *p++ = '0') ; p=malloc(strlen(hostnamebuf)+strlen(pidbuf) +strlen(timebuf)+strlen(cntbuf)+11); if (!p) { rfc2045_enomem(); return (NULL); } sprintf(p, "=_%s-%s-%s-%s", hostnamebuf, pidbuf, timebuf, cntbuf); if ((rc=rfc2045_try_boundary(s, fd, p)) == 0) break; free(p); if (rc < 0) return (NULL); } return (p); }
struct rfc2045 *rfc2045_alloc() { struct rfc2045 *p=(struct rfc2045 *)malloc(sizeof(struct rfc2045)); if (!p) { rfc2045_enomem(); return (0); } /* Initialize everything to nulls, except for one thing */ memset(p, '\0', sizeof(*p)); p->pindex=1; /* Start with part #1 */ p->workinheader=1; /* Most of the time, we're about to read a header */ return (p); }
static void doline(struct rfc2045 *p) { size_t cnt=p->workbuflen; char *c=p->workbuf; size_t n=cnt-1; /* Strip \n (we always get at least a \n here) */ struct rfc2045 *newp; struct rfc2045ac *rwp=p->rfc2045acptr; unsigned num_levels=0; size_t k; int bit8=0; if (p->numparts > MAXPARTS) { p->rfcviolation |= RFC2045_ERR2COMPLEX; return; } for (k=0; k<cnt; k++) { if (c[k] == 0) c[k]=' '; if (c[k] & 0x80) bit8=1; } if (n && c[n-1] == '\r') /* Strip trailing \r */ --n; /* Before the main drill down loop before, look ahead and see if we're ** in a middle of a form-data section. */ for (newp=p; newp->lastpart && !newp->lastpart->workclosed; newp=newp->lastpart, ++num_levels) { if (ContentBoundary(newp) == 0 || newp->workinheader) continue; if (newp->lastpart->informdata) { p=newp->lastpart; p->informdata=0; break; } } /* Drill down until we match a boundary, or until we've reached the last RFC2045 section that has been opened. */ while (p->lastpart) { size_t l; const char *cb; if (p->lastpart->workclosed) { update_counts(p, p->endpos+cnt, p->endpos+n, 1); return; } /* Leftover trash -- workclosed is set when the final ** terminating boundary has been seen */ /* content_boundary may be set before the entire header ** has been seen, so continue drilling down in that case */ cb=ContentBoundary(p); if (cb == 0 || p->workinheader) { p=p->lastpart; ++num_levels; continue; } l=strlen(cb); if (c[0] == '-' && c[1] == '-' && n >= 2+l && strncasecmp(cb, c+2, l) == 0) { if (rwp && (!p->lastpart || !p->lastpart->isdummy)) (*rwp->end_section)(); /* Ok, we've found a boundary */ if (n >= 4+l && strncmp(c+2+l, "--", 2) == 0) { /* Last boundary */ p->lastpart->workclosed=1; update_counts(p, p->endpos+cnt, p->endpos+cnt, 1); return; } /* Create new RFC2045 section */ newp=append_part(p, p->endpos+cnt); update_counts(p, p->endpos+cnt, p->endpos+n, 1); /* The new RFC2045 section is MIME compliant */ if ((newp->mime_version=strdup(p->mime_version)) == 0) rfc2045_enomem(); return; } p=p->lastpart; ++num_levels; } /* Ok, we've found the RFC2045 section that we're working with. ** No what? */ if (! p->workinheader) { /* Processing body, just update the counts. */ size_t cnt_update=cnt; if (bit8 && !p->content_8bit && (p->rfcviolation & RFC2045_ERR8BITCONTENT) == 0) { struct rfc2045 *q; for (q=p; q; q=q->parent) q->rfcviolation |= RFC2045_ERR8BITCONTENT; } /* ** In multiparts, the final newline in a part belongs to the ** boundary, otherwise, include it in the text. */ if (p->parent && p->parent->content_type && strncasecmp(p->parent->content_type, "multipart/", 10) == 0) cnt_update=n; if (!p->lastpart || !p->lastpart->workclosed) { if (rwp && !p->isdummy) (*rwp->section_contents)(c, cnt); update_counts(p, p->endpos+cnt, p->endpos+cnt_update, 1); } return; } if (bit8 && (p->rfcviolation & RFC2045_ERR8BITHEADER) == 0) { struct rfc2045 *q; for (q=p; q; q=q->parent) q->rfcviolation |= RFC2045_ERR8BITHEADER; } /* In the header */ if ( n == 0 ) /* End of header, body begins. Parse header. */ { do_header(p); /* Clean up any left over header line */ p->workinheader=0; /* Message body starts right here */ p->startbody=p->endpos+cnt; update_counts(p, p->startbody, p->startbody, 1); --p->nbodylines; /* Don't count the blank line */ /* Discard content type and boundary if I don't understand ** this MIME flavor. */ if (!RFC2045_ISMIME1(p->mime_version)) { set_string(&p->content_type, 0); rfc2045_freeattr(p->content_type_attr); p->content_type_attr=0; set_string(&p->content_disposition, 0); rfc2045_freeattr(p->content_disposition_attr); p->content_disposition_attr=0; if (p->boundary) { free(p->boundary); p->boundary=0; } } /* Normally, if we don't have a content_type, default it ** to text/plain. However, if the multipart type is ** multipart/digest, it is message/rfc822. */ if (RFC2045_ISMIME1(p->mime_version) && !p->content_type) { char *q="text/plain"; if (p->parent && p->parent->content_type && strcmp(p->parent->content_type, "multipart/digest") == 0) q="message/rfc822"; set_string(&p->content_type, q); } /* If this is not a multipart section, we don't want to ** hear about any boundaries */ if (!p->content_type || strncmp(p->content_type, "multipart/", 10)) { if (p->boundary) free(p->boundary); p->boundary=0; } /* If this section's a message, we will expect to see ** more RFC2045 stuff, so create a nested RFC2045 structure, ** and indicate that we expect to see headers. */ if (p->content_type && strcmp(p->content_type, "message/rfc822") == 0) { newp=append_part_noinherit(p, p->startbody); newp->workinheader=1; return; } /* ** If this is a multipart message (boundary defined), ** create a RFC2045 structure for the pseudo-section ** that precedes the first boundary line. */ if (ContentBoundary(p)) { newp=append_part(p, p->startbody); newp->workinheader=0; newp->isdummy=1; /* It's easier just to create it. */ return; } if (rwp) (*rwp->start_section)(p); return; } /* RFC822 header continues */ update_counts(p, p->endpos + cnt, p->endpos+n, 1); /* If this header line starts with a space, append one space ** to the saved contents of the previous line, and append this ** line to it. */ if (isspace((int)(unsigned char)*c)) { rfc2045_add_buf(&p->header, &p->headersize, &p->headerlen, " ", 1); } else { /* Otherwise the previous header line is complete, so process it */ do_header(p); p->headerlen=0; } /* Save this line in the header buffer, because the next line ** could be a continuation. */ rfc2045_add_buf( &p->header, &p->headersize, &p->headerlen, c, n); }
int rfc2045_ac_check(struct rfc2045 *p, int rwmode) { int flag=0; /* Flag - rewriting suggested */ struct rfc2045 *c; int hasnon7bit=p->has8bitchars; /* hasnon7bit: 8bit chars in this section or subsections */ const char *te; int is8bitte; for (c=p->firstpart; c; c=c->next) if (!c->isdummy) { if (rfc2045_ac_check(c, rwmode)) flag=1; if (strcmp(c->content_transfer_encoding, "7bit") && strcmp(c->content_transfer_encoding, "quoted-printable")) hasnon7bit=1; if (c->has8bitchars) p->has8bitchars=1; } if (RFC2045_ISMIME1DEF(p->mime_version) && !p->content_type) { if ((p->content_type=strdup("text/plain")) == 0) rfc2045_enomem(); if (p->mime_version) { flag=1; } } if (RFC2045_ISMIME1DEF(p->mime_version) && !rfc2045_getattr(p->content_type_attr, "charset") && strncasecmp(p->content_type, "text/", 5) == 0) { rfc2045_setattr(&p->content_type_attr, "charset", rfc2045_getdefaultcharset()); if (p->mime_version && p->firstpart == 0 /* sam - don't trigger rewrites on changes to multipart headers */ ) { flag=1; } } if (RFC2045_ISMIME1DEF(p->mime_version) && !p->content_transfer_encoding) { if ((p->content_transfer_encoding=strdup( hasnon7bit ? "8bit":"7bit")) == 0) rfc2045_enomem(); if (p->mime_version && p->firstpart == 0 /* sam - don't trigger rewrites on changes to multipart headers */ ) { flag=1; } } #if 0 if (RFC2045_ISMIME1DEF(p->mime_version) && strncmp(p->content_type, "text/", 5) == 0 && !hasnon7bit && strcmp(p->content_transfer_encoding, "7bit")) { if (p->mime_version) { flag=1; } } #endif if (RFC2045_ISMIME1DEF(p->mime_version)) { /* Check for conversions */ te=p->content_transfer_encoding; is8bitte=strcasecmp(te, "base64") && strcasecmp(te, "quoted-printable") && strcasecmp(te, "7bit"); /* 8 bit contents */ if (is8bitte && !p->has8bitchars && !p->haslongline) { if (p->rw_transfer_encoding) free(p->rw_transfer_encoding); if ((p->rw_transfer_encoding=strdup("7bit")) == 0) rfc2045_enomem(); flag=1; is8bitte=0; } if (rwmode == RFC2045_RW_7BIT && (is8bitte || p->haslongline)) { if (p->rw_transfer_encoding) free(p->rw_transfer_encoding); if ((p->rw_transfer_encoding=strdup("quoted-printable")) == 0) rfc2045_enomem(); flag=1; } else if (rwmode == RFC2045_RW_8BIT && strcasecmp(te, "quoted-printable") == 0 && !p->haslongline) { if (p->rw_transfer_encoding) free(p->rw_transfer_encoding); if ((p->rw_transfer_encoding=strdup(hasnon7bit ? "8bit":"7bit")) == 0) rfc2045_enomem(); flag=1; } } if (!p->mime_version) { if ((p->mime_version=strdup("1.0")) == 0) rfc2045_enomem(); } return (flag); }