573 lines
11 KiB
C
573 lines
11 KiB
C
#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);
|
|
}
|