blob: 3ec8d706eb2610f42a48b412ac85dc4e986b9000 [file] [log] [blame]
#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);
}