| #include "common.h" |
| #include "send.h" |
| |
| #include "../smtp/smtp.h" |
| #include "../smtp/rfc822.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)); |
| return nil; |
| } |
| |
| /* 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 |
| 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 |
| |
| /* 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_to_file"); |
| 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); |
| } |