#include "common.h"
#include "send.h"
#include <regexp.h>
#include "../smtp/smtp.h"
#include "../smtp/y.tab.h"
enum{
VMLIMIT = 64*1024,
MSGLIMIT = 128*1024*1024,
};
/* global to this file */
static Reprog *rfprog;
static Reprog *fprog;
int received; /* from rfc822.y */
static String* getstring(Node *p);
static String* getaddr(Node *p);
int
default_from(message *mp)
{
char *cp, *lp;
cp = getenv("upasname");
lp = getlog();
if(lp == nil){
free(cp);
return -1;
}
if(cp && *cp)
s_append(mp->sender, cp);
else
s_append(mp->sender, lp);
free(cp);
s_append(mp->date, thedate());
return 0;
}
message*
m_new(void)
{
message *mp;
mp = (message*)mallocz(sizeof(message), 1);
if (mp == 0)
sysfatal("m_new: %r");
mp->sender = s_new();
mp->replyaddr = s_new();
mp->date = s_new();
mp->body = s_new();
mp->size = 0;
mp->fd = -1;
return mp;
}
void
m_free(message *mp)
{
if(mp->fd >= 0)
close(mp->fd);
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(mp);
}
/* read a message into a temp file, return an open fd to it in mp->fd */
static int
m_read_to_file(Biobuf *fp, message *mp)
{
char buf[4*1024], file[Pathlen];
int fd, n;
snprint(file, sizeof file, "%s/mtXXXXXX", UPASTMP);
/*
* create temp file to be removed on close
*/
mktemp(file);
if((fd = create(file, ORDWR|ORCLOSE, 0600))<0)
return -1;
/*
* 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;
}
/* 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;
}
/* append a sub-expression match onto a String */
static void
append_match(Resub *subexp, String *sp, int se)
{
char *cp, *ep;
cp = subexp[se].sp;
ep = subexp[se].ep;
for (; cp < ep; cp++)
s_putc(sp, *cp);
s_terminate(sp);
}
static char *REMFROMRE =
"^>?From[ \t]+((\".*\")?[^\" \t]+?(\".*\")?[^\" \t]+?)[ \t]+(.+)[ \t]+remote[ \t]+from[ \t]+(.*)\n$";
static char *FROMRE =
"^>?From[ \t]+((\".*\")?[^\" \t]+?(\".*\")?[^\" \t]+?)[ \t]+(.+)\n$";
enum{
REMSENDERMATCH = 1,
SENDERMATCH = 1,
REMDATEMATCH = 4,
DATEMATCH = 4,
REMSYSMATCH = 5,
};
/* read in a message, interpret the 'From' header */
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].sp!=subexp[REMSYSMATCH].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)
sysfatal("m_read: %r");
mp->size += n;
if(n == VMLIMIT)
if(m_read_to_file(fp, mp) < 0)
sysfatal("m_read: %r");
}
/*
* 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' */
int
m_get(message *mp, vlong 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;
*pp = buf;
return pread(mp->fd, buf, sizeof buf, offset);
}
/* 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)
{
// char *p;
int rv;
String *s;
if(!returnable(s_to_c(mp->sender)))
return Bprint(fp, "From: Postmaster\n");
// p = username(s_to_c(mp->sender));
// if(p) {
// s_append(s = s_new(), p);
// 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 */
int
m_print(message *mp, Biobuf *fp, char *remote, int mbox)
{
char *date, *d, *f[6];
int n, r;
String *sender;
sender = unescapespecial(s_clone(mp->sender));
date = s_to_c(mp->date);
if(remote)
r = Bprint(fp, "From %s %s remote from %s\n", s_to_c(sender), date, remote);
else
r = Bprint(fp, "From %s %s\n", s_to_c(sender), date);
s_free(sender);
if(r < 0)
return -1;
if(!rmail && !mp->havedate){
/* add a date: line Date: Sun, 19 Apr 1998 12:27:52 -0400 */
d = strdup(date);
n = getfields(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]));
free(d);
}
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 */
int
m_bprint(message *mp, Biobuf *fp)
{
return m_noescape(mp, fp);
}
|