diff options
author | rsc <devnull@localhost> | 2005-10-29 16:26:44 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2005-10-29 16:26:44 +0000 |
commit | 5cdb17983ae6e6367ad7a940cb219eab247a9304 (patch) | |
tree | 8ca1ef49af2a96e7daebe624d91fdf679814a057 /src/cmd/upas/send/message.c | |
parent | cd3745196389579fb78b9b01ef1daefb5a57aa71 (diff) | |
download | plan9port-5cdb17983ae6e6367ad7a940cb219eab247a9304.tar.gz plan9port-5cdb17983ae6e6367ad7a940cb219eab247a9304.tar.bz2 plan9port-5cdb17983ae6e6367ad7a940cb219eab247a9304.zip |
Thanks to John Cummings.
Diffstat (limited to 'src/cmd/upas/send/message.c')
-rw-r--r-- | src/cmd/upas/send/message.c | 573 |
1 files changed, 573 insertions, 0 deletions
diff --git a/src/cmd/upas/send/message.c b/src/cmd/upas/send/message.c new file mode 100644 index 00000000..eab6160c --- /dev/null +++ b/src/cmd/upas/send/message.c @@ -0,0 +1,573 @@ +#include "common.h" +#include "send.h" + +#include "../smtp/smtp.h" +#include "../smtp/y.tab.h" + +/* global to this file */ +static Reprog *rfprog; +static Reprog *fprog; + +#define VMLIMIT (64*1024) +#define MSGLIMIT (128*1024*1024) + +int received; /* from rfc822.y */ + +static String* getstring(Node *p); +static String* getaddr(Node *p); + +extern int +default_from(message *mp) +{ + char *cp, *lp; + + cp = getenv("upasname"); + lp = getlog(); + if(lp == nil) + return -1; + + if(cp && *cp) + s_append(mp->sender, cp); + else + s_append(mp->sender, lp); + s_append(mp->date, thedate()); + return 0; +} + +extern message * +m_new(void) +{ + message *mp; + + mp = (message *)mallocz(sizeof(message), 1); + if (mp == 0) { + perror("message:"); + exit(1); + } + mp->sender = s_new(); + mp->replyaddr = s_new(); + mp->date = s_new(); + mp->body = s_new(); + mp->size = 0; + mp->fd = -1; + return mp; +} + +extern void +m_free(message *mp) +{ + if(mp->fd >= 0){ + close(mp->fd); + sysremove(s_to_c(mp->tmp)); + s_free(mp->tmp); + } + s_free(mp->sender); + s_free(mp->date); + s_free(mp->body); + s_free(mp->havefrom); + s_free(mp->havesender); + s_free(mp->havereplyto); + s_free(mp->havesubject); + free((char *)mp); +} + +/* read a message into a temp file , return an open fd to it */ +static int +m_read_to_file(Biobuf *fp, message *mp) +{ + int fd; + int n; + String *file; + char buf[4*1024]; + + file = s_new(); + /* + * create temp file to be remove on close + */ + abspath("mtXXXXXX", UPASTMP, file); + mktemp(s_to_c(file)); + if((fd = syscreate(s_to_c(file), ORDWR|ORCLOSE, 0600))<0){ + s_free(file); + return -1; + } + mp->tmp = file; + + /* + * read the rest into the temp file + */ + while((n = Bread(fp, buf, sizeof(buf))) > 0){ + if(write(fd, buf, n) != n){ + close(fd); + return -1; + } + mp->size += n; + if(mp->size > MSGLIMIT){ + mp->size = -1; + break; + } + } + + mp->fd = fd; + return 0; +} + +/* get the first address from a node */ +static String* +getaddr(Node *p) +{ + for(; p; p = p->next) + if(p->s && p->addr) + return s_copy(s_to_c(p->s)); +} + +/* get the text of a header line minus the field name */ +static String* +getstring(Node *p) +{ + String *s; + + s = s_new(); + if(p == nil) + return s; + + for(p = p->next; p; p = p->next){ + if(p->s){ + s_append(s, s_to_c(p->s)); + }else{ + s_putc(s, p->c); + s_terminate(s); + } + if(p->white) + s_append(s, s_to_c(p->white)); + } + return s; +} + +#if 0 /* jpc */ +static char *fieldname[] = +{ +[WORD-WORD] "WORD", +[DATE-WORD] "DATE", +[RESENT_DATE-WORD] "RESENT_DATE", +[RETURN_PATH-WORD] "RETURN_PATH", +[FROM-WORD] "FROM", +[SENDER-WORD] "SENDER", +[REPLY_TO-WORD] "REPLY_TO", +[RESENT_FROM-WORD] "RESENT_FROM", +[RESENT_SENDER-WORD] "RESENT_SENDER", +[RESENT_REPLY_TO-WORD] "RESENT_REPLY_TO", +[SUBJECT-WORD] "SUBJECT", +[TO-WORD] "TO", +[CC-WORD] "CC", +[BCC-WORD] "BCC", +[RESENT_TO-WORD] "RESENT_TO", +[RESENT_CC-WORD] "RESENT_CC", +[RESENT_BCC-WORD] "RESENT_BCC", +[REMOTE-WORD] "REMOTE", +[PRECEDENCE-WORD] "PRECEDENCE", +[MIMEVERSION-WORD] "MIMEVERSION", +[CONTENTTYPE-WORD] "CONTENTTYPE", +[MESSAGEID-WORD] "MESSAGEID", +[RECEIVED-WORD] "RECEIVED", +[MAILER-WORD] "MAILER", +[BADTOKEN-WORD] "BADTOKEN", +}; +#endif /* jpc */ + +/* fix 822 addresses */ +static void +rfc822cruft(message *mp) +{ + Field *f; + Node *p; + String *body, *s; + char *cp; + + /* + * parse headers in in-core part + */ + yyinit(s_to_c(mp->body), s_len(mp->body)); + mp->rfc822headers = 0; + yyparse(); + mp->rfc822headers = 1; + mp->received = received; + + /* + * remove equivalent systems in all addresses + */ + body = s_new(); + cp = s_to_c(mp->body); + for(f = firstfield; f; f = f->next){ + if(f->node->c == MIMEVERSION) + mp->havemime = 1; + if(f->node->c == FROM) + mp->havefrom = getaddr(f->node); + if(f->node->c == SENDER) + mp->havesender = getaddr(f->node); + if(f->node->c == REPLY_TO) + mp->havereplyto = getaddr(f->node); + if(f->node->c == TO) + mp->haveto = 1; + if(f->node->c == DATE) + mp->havedate = 1; + if(f->node->c == SUBJECT) + mp->havesubject = getstring(f->node); + if(f->node->c == PRECEDENCE && f->node->next && f->node->next->next){ + s = f->node->next->next->s; + if(s && (strcmp(s_to_c(s), "bulk") == 0 + || strcmp(s_to_c(s), "Bulk") == 0)) + mp->bulk = 1; + } + for(p = f->node; p; p = p->next){ + if(p->s){ + if(p->addr){ + cp = skipequiv(s_to_c(p->s)); + s_append(body, cp); + } else + s_append(body, s_to_c(p->s)); + }else{ + s_putc(body, p->c); + s_terminate(body); + } + if(p->white) + s_append(body, s_to_c(p->white)); + cp = p->end+1; + } + s_append(body, "\n"); + } + + if(*s_to_c(body) == 0){ + s_free(body); + return; + } + + if(*cp != '\n') + s_append(body, "\n"); + s_memappend(body, cp, s_len(mp->body) - (cp - s_to_c(mp->body))); + s_terminate(body); + + firstfield = 0; + mp->size += s_len(body) - s_len(mp->body); + s_free(mp->body); + mp->body = body; +} + +/* read in a message, interpret the 'From' header */ +extern message * +m_read(Biobuf *fp, int rmail, int interactive) +{ + message *mp; + Resub subexp[10]; + char *line; + int first; + int n; + + mp = m_new(); + + /* parse From lines if remote */ + if (rmail) { + /* get remote address */ + String *sender=s_new(); + + if (rfprog == 0) + rfprog = regcomp(REMFROMRE); + first = 1; + while(s_read_line(fp, s_restart(mp->body)) != 0) { + memset(subexp, 0, sizeof(subexp)); + if (regexec(rfprog, s_to_c(mp->body), subexp, 10) == 0){ + if(first == 0) + break; + if (fprog == 0) + fprog = regcomp(FROMRE); + memset(subexp, 0, sizeof(subexp)); + if(regexec(fprog, s_to_c(mp->body), subexp,10) == 0) + break; + s_restart(mp->body); + append_match(subexp, s_restart(sender), SENDERMATCH); + append_match(subexp, s_restart(mp->date), DATEMATCH); + break; + } + append_match(subexp, s_restart(sender), REMSENDERMATCH); + append_match(subexp, s_restart(mp->date), REMDATEMATCH); + if(subexp[REMSYSMATCH].s.sp!=subexp[REMSYSMATCH].e.ep){ + append_match(subexp, mp->sender, REMSYSMATCH); + s_append(mp->sender, "!"); + } + first = 0; + } + s_append(mp->sender, s_to_c(sender)); + + s_free(sender); + } + if(*s_to_c(mp->sender)=='\0') + default_from(mp); + + /* if sender address is unreturnable, treat message as bulk mail */ + if(!returnable(s_to_c(mp->sender))) + mp->bulk = 1; + + /* get body */ + if(interactive && !rmail){ + /* user typing on terminal: terminator == '.' or EOF */ + for(;;) { + line = s_read_line(fp, mp->body); + if (line == 0) + break; + if (strcmp(".\n", line)==0) { + mp->body->ptr -= 2; + *mp->body->ptr = '\0'; + break; + } + } + mp->size = mp->body->ptr - mp->body->base; + } else { + /* + * read up to VMLIMIT bytes (more or less) into main memory. + * if message is longer put the rest in a tmp file. + */ + mp->size = mp->body->ptr - mp->body->base; + n = s_read(fp, mp->body, VMLIMIT); + if(n < 0){ + perror("m_read"); + exit(1); + } + mp->size += n; + if(n == VMLIMIT){ + if(m_read_to_file(fp, mp) < 0){ + perror("m_read"); + exit(1); + } + } + + } + + /* + * ignore 0 length messages from a terminal + */ + if (!rmail && mp->size == 0) + return 0; + + rfc822cruft(mp); + + return mp; +} + +/* return a piece of message starting at `offset' */ +extern int +m_get(message *mp, long offset, char **pp) +{ + static char buf[4*1024]; + + /* + * are we past eof? + */ + if(offset >= mp->size) + return 0; + + /* + * are we in the virtual memory portion? + */ + if(offset < s_len(mp->body)){ + *pp = mp->body->base + offset; + return mp->body->ptr - mp->body->base - offset; + } + + /* + * read it from the temp file + */ + offset -= s_len(mp->body); + if(mp->fd < 0) + return -1; + if(seek(mp->fd, offset, 0)<0) + return -1; + *pp = buf; + return read(mp->fd, buf, sizeof buf); +} + +/* output the message body without ^From escapes */ +static int +m_noescape(message *mp, Biobuf *fp) +{ + long offset; + int n; + char *p; + + for(offset = 0; offset < mp->size; offset += n){ + n = m_get(mp, offset, &p); + if(n <= 0){ + Bflush(fp); + return -1; + } + if(Bwrite(fp, p, n) < 0) + return -1; + } + return Bflush(fp); +} + +/* + * Output the message body with '^From ' escapes. + * Ensures that any line starting with a 'From ' gets a ' ' stuck + * in front of it. + */ +static int +m_escape(message *mp, Biobuf *fp) +{ + char *p, *np; + char *end; + long offset; + int m, n; + char *start; + + for(offset = 0; offset < mp->size; offset += n){ + n = m_get(mp, offset, &start); + if(n < 0){ + Bflush(fp); + return -1; + } + + p = start; + for(end = p+n; p < end; p += m){ + np = memchr(p, '\n', end-p); + if(np == 0){ + Bwrite(fp, p, end-p); + break; + } + m = np - p + 1; + if(m > 5 && strncmp(p, "From ", 5) == 0) + Bputc(fp, ' '); + Bwrite(fp, p, m); + } + } + Bflush(fp); + return 0; +} + +static int +printfrom(message *mp, Biobuf *fp) +{ + String *s; + int rv; + + if(!returnable(s_to_c(mp->sender))) + return Bprint(fp, "From: Postmaster\n"); + + s = username(mp->sender); + if(s) { + s_append(s, " <"); + s_append(s, s_to_c(mp->sender)); + s_append(s, ">"); + } else { + s = s_copy(s_to_c(mp->sender)); + } + s = unescapespecial(s); + rv = Bprint(fp, "From: %s\n", s_to_c(s)); + s_free(s); + return rv; +} + +static char * +rewritezone(char *z) +{ + int mindiff; + char s; + Tm *tm; + static char x[7]; + + tm = localtime(time(0)); + mindiff = tm->tzoff/60; + + /* if not in my timezone, don't change anything */ + if(strcmp(tm->zone, z) != 0) + return z; + + if(mindiff < 0){ + s = '-'; + mindiff = -mindiff; + } else + s = '+'; + + sprint(x, "%c%.2d%.2d", s, mindiff/60, mindiff%60); + return x; +} + +int +isutf8(String *s) +{ + char *p; + + for(p = s_to_c(s); *p; p++) + if(*p&0x80) + return 1; + return 0; +} + +void +printutf8mime(Biobuf *b) +{ + Bprint(b, "MIME-Version: 1.0\n"); + Bprint(b, "Content-Type: text/plain; charset=\"UTF-8\"\n"); + Bprint(b, "Content-Transfer-Encoding: 8bit\n"); +} + +/* output a message */ +extern int +m_print(message *mp, Biobuf *fp, char *remote, int mbox) +{ + String *date, *sender; + char *f[6]; + int n; + + sender = unescapespecial(s_clone(mp->sender)); + + if (remote != 0){ + if(print_remote_header(fp,s_to_c(sender),s_to_c(mp->date),remote) < 0){ + s_free(sender); + return -1; + } + } else { + if(print_header(fp, s_to_c(sender), s_to_c(mp->date)) < 0){ + s_free(sender); + return -1; + } + } + s_free(sender); + if(!rmail && !mp->havedate){ + /* add a date: line Date: Sun, 19 Apr 1998 12:27:52 -0400 */ + date = s_copy(s_to_c(mp->date)); + n = getfields(s_to_c(date), f, 6, 1, " \t"); + if(n == 6) + Bprint(fp, "Date: %s, %s %s %s %s %s\n", f[0], f[2], f[1], + f[5], f[3], rewritezone(f[4])); + } + if(!rmail && !mp->havemime && isutf8(mp->body)) + printutf8mime(fp); + if(mp->to){ + /* add the to: line */ + if (Bprint(fp, "%s\n", s_to_c(mp->to)) < 0) + return -1; + /* add the from: line */ + if (!mp->havefrom && printfrom(mp, fp) < 0) + return -1; + if(!mp->rfc822headers && *s_to_c(mp->body) != '\n') + if (Bprint(fp, "\n") < 0) + return -1; + } else if(!rmail){ + /* add the from: line */ + if (!mp->havefrom && printfrom(mp, fp) < 0) + return -1; + if(!mp->rfc822headers && *s_to_c(mp->body) != '\n') + if (Bprint(fp, "\n") < 0) + return -1; + } + + if (!mbox) + return m_noescape(mp, fp); + return m_escape(mp, fp); +} + +/* print just the message body */ +extern int +m_bprint(message *mp, Biobuf *fp) +{ + return m_noescape(mp, fp); +} |