xref: /plan9/sys/src/cmd/upas/send/message.c (revision c6569576083e48cef148efbb162a33bacec4ce98)
13e12c5d1SDavid du Colombier #include "common.h"
23e12c5d1SDavid du Colombier #include "send.h"
33e12c5d1SDavid du Colombier 
47dd7cddfSDavid du Colombier #include "../smtp/smtp.h"
57dd7cddfSDavid du Colombier #include "../smtp/y.tab.h"
67dd7cddfSDavid du Colombier 
73e12c5d1SDavid du Colombier /* global to this file */
83e12c5d1SDavid du Colombier static Reprog *rfprog;
93e12c5d1SDavid du Colombier static Reprog *fprog;
103e12c5d1SDavid du Colombier 
113e12c5d1SDavid du Colombier #define VMLIMIT (64*1024)
12219b2ee8SDavid du Colombier #define MSGLIMIT (128*1024*1024)
133e12c5d1SDavid du Colombier 
147dd7cddfSDavid du Colombier int received;	/* from rfc822.y */
157dd7cddfSDavid du Colombier 
169a747e4fSDavid du Colombier static String*	getstring(Node *p);
179a747e4fSDavid du Colombier static String*	getaddr(Node *p);
189a747e4fSDavid du Colombier 
197dd7cddfSDavid du Colombier extern int
default_from(message * mp)203e12c5d1SDavid du Colombier default_from(message *mp)
213e12c5d1SDavid du Colombier {
227dd7cddfSDavid du Colombier 	char *cp, *lp;
233e12c5d1SDavid du Colombier 
243e12c5d1SDavid du Colombier 	cp = getenv("upasname");
257dd7cddfSDavid du Colombier 	lp = getlog();
267dd7cddfSDavid du Colombier 	if(lp == nil)
277dd7cddfSDavid du Colombier 		return -1;
287dd7cddfSDavid du Colombier 
297dd7cddfSDavid du Colombier 	if(cp && *cp)
303e12c5d1SDavid du Colombier 		s_append(mp->sender, cp);
313e12c5d1SDavid du Colombier 	else
327dd7cddfSDavid du Colombier 		s_append(mp->sender, lp);
333e12c5d1SDavid du Colombier 	s_append(mp->date, thedate());
347dd7cddfSDavid du Colombier 	return 0;
353e12c5d1SDavid du Colombier }
363e12c5d1SDavid du Colombier 
373e12c5d1SDavid du Colombier extern message *
m_new(void)383e12c5d1SDavid du Colombier m_new(void)
393e12c5d1SDavid du Colombier {
403e12c5d1SDavid du Colombier 	message *mp;
413e12c5d1SDavid du Colombier 
4268060204SDavid du Colombier 	mp = (message *)mallocz(sizeof(message), 1);
433e12c5d1SDavid du Colombier 	if (mp == 0) {
443e12c5d1SDavid du Colombier 		perror("message:");
453e12c5d1SDavid du Colombier 		exit(1);
463e12c5d1SDavid du Colombier 	}
473e12c5d1SDavid du Colombier 	mp->sender = s_new();
483e12c5d1SDavid du Colombier 	mp->replyaddr = s_new();
493e12c5d1SDavid du Colombier 	mp->date = s_new();
503e12c5d1SDavid du Colombier 	mp->body = s_new();
513e12c5d1SDavid du Colombier 	mp->size = 0;
523e12c5d1SDavid du Colombier 	mp->fd = -1;
533e12c5d1SDavid du Colombier 	return mp;
543e12c5d1SDavid du Colombier }
553e12c5d1SDavid du Colombier 
563e12c5d1SDavid du Colombier extern void
m_free(message * mp)573e12c5d1SDavid du Colombier m_free(message *mp)
583e12c5d1SDavid du Colombier {
593e12c5d1SDavid du Colombier 	if(mp->fd >= 0){
603e12c5d1SDavid du Colombier 		close(mp->fd);
617dd7cddfSDavid du Colombier 		sysremove(s_to_c(mp->tmp));
623e12c5d1SDavid du Colombier 		s_free(mp->tmp);
633e12c5d1SDavid du Colombier 	}
643e12c5d1SDavid du Colombier 	s_free(mp->sender);
653e12c5d1SDavid du Colombier 	s_free(mp->date);
663e12c5d1SDavid du Colombier 	s_free(mp->body);
679a747e4fSDavid du Colombier 	s_free(mp->havefrom);
689a747e4fSDavid du Colombier 	s_free(mp->havesender);
699a747e4fSDavid du Colombier 	s_free(mp->havereplyto);
709a747e4fSDavid du Colombier 	s_free(mp->havesubject);
713e12c5d1SDavid du Colombier 	free((char *)mp);
723e12c5d1SDavid du Colombier }
733e12c5d1SDavid du Colombier 
74*c6569576SDavid du Colombier /* read a message into a temp file, return an open fd to it in mp->fd */
753e12c5d1SDavid du Colombier static int
m_read_to_file(Biobuf * fp,message * mp)763e12c5d1SDavid du Colombier m_read_to_file(Biobuf *fp, message *mp)
773e12c5d1SDavid du Colombier {
783e12c5d1SDavid du Colombier 	int fd;
793e12c5d1SDavid du Colombier 	int n;
803e12c5d1SDavid du Colombier 	String *file;
813e12c5d1SDavid du Colombier 	char buf[4*1024];
823e12c5d1SDavid du Colombier 
833e12c5d1SDavid du Colombier 	file = s_new();
843e12c5d1SDavid du Colombier 	/*
85*c6569576SDavid du Colombier 	 *  create temp file to be removed on close
863e12c5d1SDavid du Colombier 	 */
877dd7cddfSDavid du Colombier 	abspath("mtXXXXXX", UPASTMP, file);
883e12c5d1SDavid du Colombier 	mktemp(s_to_c(file));
897dd7cddfSDavid du Colombier 	if((fd = syscreate(s_to_c(file), ORDWR|ORCLOSE, 0600))<0){
907dd7cddfSDavid du Colombier 		s_free(file);
913e12c5d1SDavid du Colombier 		return -1;
923e12c5d1SDavid du Colombier 	}
933e12c5d1SDavid du Colombier 	mp->tmp = file;
943e12c5d1SDavid du Colombier 
953e12c5d1SDavid du Colombier 	/*
963e12c5d1SDavid du Colombier 	 *  read the rest into the temp file
973e12c5d1SDavid du Colombier 	 */
983e12c5d1SDavid du Colombier 	while((n = Bread(fp, buf, sizeof(buf))) > 0){
993e12c5d1SDavid du Colombier 		if(write(fd, buf, n) != n){
1003e12c5d1SDavid du Colombier 			close(fd);
1013e12c5d1SDavid du Colombier 			return -1;
1023e12c5d1SDavid du Colombier 		}
1033e12c5d1SDavid du Colombier 		mp->size += n;
1043e12c5d1SDavid du Colombier 		if(mp->size > MSGLIMIT){
1053e12c5d1SDavid du Colombier 			mp->size = -1;
1063e12c5d1SDavid du Colombier 			break;
1073e12c5d1SDavid du Colombier 		}
1083e12c5d1SDavid du Colombier 	}
1093e12c5d1SDavid du Colombier 
1103e12c5d1SDavid du Colombier 	mp->fd = fd;
1113e12c5d1SDavid du Colombier 	return 0;
1123e12c5d1SDavid du Colombier }
1133e12c5d1SDavid du Colombier 
1149a747e4fSDavid du Colombier /* get the first address from a node */
1159a747e4fSDavid du Colombier static String*
getaddr(Node * p)1169a747e4fSDavid du Colombier getaddr(Node *p)
1179a747e4fSDavid du Colombier {
1189a747e4fSDavid du Colombier 	for(; p; p = p->next)
1199a747e4fSDavid du Colombier 		if(p->s && p->addr)
1209a747e4fSDavid du Colombier 			return s_copy(s_to_c(p->s));
121ec46fab0SDavid du Colombier 	return nil;
1229a747e4fSDavid du Colombier }
1239a747e4fSDavid du Colombier 
1249a747e4fSDavid du Colombier /* get the text of a header line minus the field name */
1259a747e4fSDavid du Colombier static String*
getstring(Node * p)1269a747e4fSDavid du Colombier getstring(Node *p)
1279a747e4fSDavid du Colombier {
1289a747e4fSDavid du Colombier 	String *s;
1299a747e4fSDavid du Colombier 
1309a747e4fSDavid du Colombier 	s = s_new();
1319a747e4fSDavid du Colombier 	if(p == nil)
1329a747e4fSDavid du Colombier 		return s;
1339a747e4fSDavid du Colombier 
1349a747e4fSDavid du Colombier 	for(p = p->next; p; p = p->next){
1359a747e4fSDavid du Colombier 		if(p->s){
1369a747e4fSDavid du Colombier 			s_append(s, s_to_c(p->s));
1379a747e4fSDavid du Colombier 		}else{
1389a747e4fSDavid du Colombier 			s_putc(s, p->c);
1399a747e4fSDavid du Colombier 			s_terminate(s);
1409a747e4fSDavid du Colombier 		}
1419a747e4fSDavid du Colombier 		if(p->white)
1429a747e4fSDavid du Colombier 			s_append(s, s_to_c(p->white));
1439a747e4fSDavid du Colombier 	}
1449a747e4fSDavid du Colombier 	return s;
1459a747e4fSDavid du Colombier }
1469a747e4fSDavid du Colombier 
1479a747e4fSDavid du Colombier static char *fieldname[] =
1489a747e4fSDavid du Colombier {
1499a747e4fSDavid du Colombier [WORD-WORD]	"WORD",
1509a747e4fSDavid du Colombier [DATE-WORD]	"DATE",
1519a747e4fSDavid du Colombier [RESENT_DATE-WORD]	"RESENT_DATE",
1529a747e4fSDavid du Colombier [RETURN_PATH-WORD]	"RETURN_PATH",
1539a747e4fSDavid du Colombier [FROM-WORD]	"FROM",
1549a747e4fSDavid du Colombier [SENDER-WORD]	"SENDER",
1559a747e4fSDavid du Colombier [REPLY_TO-WORD]	"REPLY_TO",
1569a747e4fSDavid du Colombier [RESENT_FROM-WORD]	"RESENT_FROM",
1579a747e4fSDavid du Colombier [RESENT_SENDER-WORD]	"RESENT_SENDER",
1589a747e4fSDavid du Colombier [RESENT_REPLY_TO-WORD]	"RESENT_REPLY_TO",
1599a747e4fSDavid du Colombier [SUBJECT-WORD]	"SUBJECT",
1609a747e4fSDavid du Colombier [TO-WORD]	"TO",
1619a747e4fSDavid du Colombier [CC-WORD]	"CC",
1629a747e4fSDavid du Colombier [BCC-WORD]	"BCC",
1639a747e4fSDavid du Colombier [RESENT_TO-WORD]	"RESENT_TO",
1649a747e4fSDavid du Colombier [RESENT_CC-WORD]	"RESENT_CC",
1659a747e4fSDavid du Colombier [RESENT_BCC-WORD]	"RESENT_BCC",
1669a747e4fSDavid du Colombier [REMOTE-WORD]	"REMOTE",
1679a747e4fSDavid du Colombier [PRECEDENCE-WORD]	"PRECEDENCE",
1689a747e4fSDavid du Colombier [MIMEVERSION-WORD]	"MIMEVERSION",
1699a747e4fSDavid du Colombier [CONTENTTYPE-WORD]	"CONTENTTYPE",
1709a747e4fSDavid du Colombier [MESSAGEID-WORD]	"MESSAGEID",
1719a747e4fSDavid du Colombier [RECEIVED-WORD]	"RECEIVED",
1729a747e4fSDavid du Colombier [MAILER-WORD]	"MAILER",
1739a747e4fSDavid du Colombier [BADTOKEN-WORD]	"BADTOKEN",
1749a747e4fSDavid du Colombier };
1759a747e4fSDavid du Colombier 
1767dd7cddfSDavid du Colombier /* fix 822 addresses */
1777dd7cddfSDavid du Colombier static void
rfc822cruft(message * mp)1787dd7cddfSDavid du Colombier rfc822cruft(message *mp)
1797dd7cddfSDavid du Colombier {
1807dd7cddfSDavid du Colombier 	Field *f;
1817dd7cddfSDavid du Colombier 	Node *p;
1827dd7cddfSDavid du Colombier 	String *body, *s;
1837dd7cddfSDavid du Colombier 	char *cp;
1847dd7cddfSDavid du Colombier 
1857dd7cddfSDavid du Colombier 	/*
1867dd7cddfSDavid du Colombier 	 *  parse headers in in-core part
1877dd7cddfSDavid du Colombier 	 */
1889a747e4fSDavid du Colombier 	yyinit(s_to_c(mp->body), s_len(mp->body));
1897dd7cddfSDavid du Colombier 	mp->rfc822headers = 0;
1907dd7cddfSDavid du Colombier 	yyparse();
1917dd7cddfSDavid du Colombier 	mp->rfc822headers = 1;
1927dd7cddfSDavid du Colombier 	mp->received = received;
1937dd7cddfSDavid du Colombier 
1947dd7cddfSDavid du Colombier 	/*
1957dd7cddfSDavid du Colombier 	 *  remove equivalent systems in all addresses
1967dd7cddfSDavid du Colombier 	 */
1977dd7cddfSDavid du Colombier 	body = s_new();
1987dd7cddfSDavid du Colombier 	cp = s_to_c(mp->body);
1997dd7cddfSDavid du Colombier 	for(f = firstfield; f; f = f->next){
2007dd7cddfSDavid du Colombier 		if(f->node->c == MIMEVERSION)
2017dd7cddfSDavid du Colombier 			mp->havemime = 1;
2027dd7cddfSDavid du Colombier 		if(f->node->c == FROM)
2039a747e4fSDavid du Colombier 			mp->havefrom = getaddr(f->node);
2049a747e4fSDavid du Colombier 		if(f->node->c == SENDER)
2059a747e4fSDavid du Colombier 			mp->havesender = getaddr(f->node);
2069a747e4fSDavid du Colombier 		if(f->node->c == REPLY_TO)
2079a747e4fSDavid du Colombier 			mp->havereplyto = getaddr(f->node);
2087dd7cddfSDavid du Colombier 		if(f->node->c == TO)
2097dd7cddfSDavid du Colombier 			mp->haveto = 1;
2107dd7cddfSDavid du Colombier 		if(f->node->c == DATE)
2117dd7cddfSDavid du Colombier 			mp->havedate = 1;
2127dd7cddfSDavid du Colombier 		if(f->node->c == SUBJECT)
2139a747e4fSDavid du Colombier 			mp->havesubject = getstring(f->node);
2147dd7cddfSDavid du Colombier 		if(f->node->c == PRECEDENCE && f->node->next && f->node->next->next){
2157dd7cddfSDavid du Colombier 			s = f->node->next->next->s;
2167dd7cddfSDavid du Colombier 			if(s && (strcmp(s_to_c(s), "bulk") == 0
2177dd7cddfSDavid du Colombier 				|| strcmp(s_to_c(s), "Bulk") == 0))
2187dd7cddfSDavid du Colombier 					mp->bulk = 1;
2197dd7cddfSDavid du Colombier 		}
2207dd7cddfSDavid du Colombier 		for(p = f->node; p; p = p->next){
2217dd7cddfSDavid du Colombier 			if(p->s){
2227dd7cddfSDavid du Colombier 				if(p->addr){
2237dd7cddfSDavid du Colombier 					cp = skipequiv(s_to_c(p->s));
2247dd7cddfSDavid du Colombier 					s_append(body, cp);
2257dd7cddfSDavid du Colombier 				} else
2267dd7cddfSDavid du Colombier 					s_append(body, s_to_c(p->s));
2277dd7cddfSDavid du Colombier 			}else{
2287dd7cddfSDavid du Colombier 				s_putc(body, p->c);
2297dd7cddfSDavid du Colombier 				s_terminate(body);
2307dd7cddfSDavid du Colombier 			}
2317dd7cddfSDavid du Colombier 			if(p->white)
2327dd7cddfSDavid du Colombier 				s_append(body, s_to_c(p->white));
2337dd7cddfSDavid du Colombier 			cp = p->end+1;
2347dd7cddfSDavid du Colombier 		}
2357dd7cddfSDavid du Colombier 		s_append(body, "\n");
2367dd7cddfSDavid du Colombier 	}
2377dd7cddfSDavid du Colombier 
2387dd7cddfSDavid du Colombier 	if(*s_to_c(body) == 0){
2397dd7cddfSDavid du Colombier 		s_free(body);
2407dd7cddfSDavid du Colombier 		return;
2417dd7cddfSDavid du Colombier 	}
2427dd7cddfSDavid du Colombier 
2437dd7cddfSDavid du Colombier 	if(*cp != '\n')
2447dd7cddfSDavid du Colombier 		s_append(body, "\n");
2457dd7cddfSDavid du Colombier 	s_memappend(body, cp, s_len(mp->body) - (cp - s_to_c(mp->body)));
2467dd7cddfSDavid du Colombier 	s_terminate(body);
2477dd7cddfSDavid du Colombier 
2487dd7cddfSDavid du Colombier 	firstfield = 0;
2497dd7cddfSDavid du Colombier 	mp->size += s_len(body) - s_len(mp->body);
2507dd7cddfSDavid du Colombier 	s_free(mp->body);
2517dd7cddfSDavid du Colombier 	mp->body = body;
2527dd7cddfSDavid du Colombier }
2537dd7cddfSDavid du Colombier 
2543e12c5d1SDavid du Colombier /* read in a message, interpret the 'From' header */
2553e12c5d1SDavid du Colombier extern message *
m_read(Biobuf * fp,int rmail,int interactive)2567dd7cddfSDavid du Colombier m_read(Biobuf *fp, int rmail, int interactive)
2573e12c5d1SDavid du Colombier {
2583e12c5d1SDavid du Colombier 	message *mp;
2593e12c5d1SDavid du Colombier 	Resub subexp[10];
2607dd7cddfSDavid du Colombier 	char *line;
2613e12c5d1SDavid du Colombier 	int first;
2623e12c5d1SDavid du Colombier 	int n;
2633e12c5d1SDavid du Colombier 
2643e12c5d1SDavid du Colombier 	mp = m_new();
2653e12c5d1SDavid du Colombier 
2663e12c5d1SDavid du Colombier 	/* parse From lines if remote */
2673e12c5d1SDavid du Colombier 	if (rmail) {
2683e12c5d1SDavid du Colombier 		/* get remote address */
2693e12c5d1SDavid du Colombier 		String *sender=s_new();
2703e12c5d1SDavid du Colombier 
2713e12c5d1SDavid du Colombier 		if (rfprog == 0)
2723e12c5d1SDavid du Colombier 			rfprog = regcomp(REMFROMRE);
2733e12c5d1SDavid du Colombier 		first = 1;
2743e12c5d1SDavid du Colombier 		while(s_read_line(fp, s_restart(mp->body)) != 0) {
2753e12c5d1SDavid du Colombier 			memset(subexp, 0, sizeof(subexp));
2763e12c5d1SDavid du Colombier 			if (regexec(rfprog, s_to_c(mp->body), subexp, 10) == 0){
2773e12c5d1SDavid du Colombier 				if(first == 0)
2783e12c5d1SDavid du Colombier 					break;
2793e12c5d1SDavid du Colombier 				if (fprog == 0)
2803e12c5d1SDavid du Colombier 					fprog = regcomp(FROMRE);
2813e12c5d1SDavid du Colombier 				memset(subexp, 0, sizeof(subexp));
2823e12c5d1SDavid du Colombier 				if(regexec(fprog, s_to_c(mp->body), subexp,10) == 0)
2833e12c5d1SDavid du Colombier 					break;
2849a747e4fSDavid du Colombier 				s_restart(mp->body);
2853e12c5d1SDavid du Colombier 				append_match(subexp, s_restart(sender), SENDERMATCH);
2863e12c5d1SDavid du Colombier 				append_match(subexp, s_restart(mp->date), DATEMATCH);
2873e12c5d1SDavid du Colombier 				break;
2883e12c5d1SDavid du Colombier 			}
2893e12c5d1SDavid du Colombier 			append_match(subexp, s_restart(sender), REMSENDERMATCH);
2903e12c5d1SDavid du Colombier 			append_match(subexp, s_restart(mp->date), REMDATEMATCH);
2913e12c5d1SDavid du Colombier 			if(subexp[REMSYSMATCH].sp!=subexp[REMSYSMATCH].ep){
2923e12c5d1SDavid du Colombier 				append_match(subexp, mp->sender, REMSYSMATCH);
2933e12c5d1SDavid du Colombier 				s_append(mp->sender, "!");
2943e12c5d1SDavid du Colombier 			}
2953e12c5d1SDavid du Colombier 			first = 0;
2963e12c5d1SDavid du Colombier 		}
2973e12c5d1SDavid du Colombier 		s_append(mp->sender, s_to_c(sender));
2987dd7cddfSDavid du Colombier 
2993e12c5d1SDavid du Colombier 		s_free(sender);
3003e12c5d1SDavid du Colombier 	}
3013e12c5d1SDavid du Colombier 	if(*s_to_c(mp->sender)=='\0')
3023e12c5d1SDavid du Colombier 		default_from(mp);
3033e12c5d1SDavid du Colombier 
3047dd7cddfSDavid du Colombier 	/* if sender address is unreturnable, treat message as bulk mail */
3057dd7cddfSDavid du Colombier 	if(!returnable(s_to_c(mp->sender)))
3067dd7cddfSDavid du Colombier 		mp->bulk = 1;
3077dd7cddfSDavid du Colombier 
3087dd7cddfSDavid du Colombier 	/* get body */
3097dd7cddfSDavid du Colombier 	if(interactive && !rmail){
3107dd7cddfSDavid du Colombier 		/* user typing on terminal: terminator == '.' or EOF */
3117dd7cddfSDavid du Colombier 		for(;;) {
3127dd7cddfSDavid du Colombier 			line = s_read_line(fp, mp->body);
3137dd7cddfSDavid du Colombier 			if (line == 0)
3147dd7cddfSDavid du Colombier 				break;
3157dd7cddfSDavid du Colombier 			if (strcmp(".\n", line)==0) {
3167dd7cddfSDavid du Colombier 				mp->body->ptr -= 2;
3177dd7cddfSDavid du Colombier 				*mp->body->ptr = '\0';
3187dd7cddfSDavid du Colombier 				break;
3197dd7cddfSDavid du Colombier 			}
3207dd7cddfSDavid du Colombier 		}
3217dd7cddfSDavid du Colombier 		mp->size = mp->body->ptr - mp->body->base;
3227dd7cddfSDavid du Colombier 	} else {
3233e12c5d1SDavid du Colombier 		/*
3243e12c5d1SDavid du Colombier 		 *  read up to VMLIMIT bytes (more or less) into main memory.
3253e12c5d1SDavid du Colombier 		 *  if message is longer put the rest in a tmp file.
3263e12c5d1SDavid du Colombier 		 */
3273e12c5d1SDavid du Colombier 		mp->size = mp->body->ptr - mp->body->base;
3283e12c5d1SDavid du Colombier 		n = s_read(fp, mp->body, VMLIMIT);
3293e12c5d1SDavid du Colombier 		if(n < 0){
3303e12c5d1SDavid du Colombier 			perror("m_read");
3313e12c5d1SDavid du Colombier 			exit(1);
3323e12c5d1SDavid du Colombier 		}
3333e12c5d1SDavid du Colombier 		mp->size += n;
3343e12c5d1SDavid du Colombier 		if(n == VMLIMIT){
3353e12c5d1SDavid du Colombier 			if(m_read_to_file(fp, mp) < 0){
3363e12c5d1SDavid du Colombier 				perror("m_read");
3373e12c5d1SDavid du Colombier 				exit(1);
3383e12c5d1SDavid du Colombier 			}
3393e12c5d1SDavid du Colombier 		}
3403e12c5d1SDavid du Colombier 
3417dd7cddfSDavid du Colombier 	}
3427dd7cddfSDavid du Colombier 
3433e12c5d1SDavid du Colombier 	/*
3443e12c5d1SDavid du Colombier 	 *  ignore 0 length messages from a terminal
3453e12c5d1SDavid du Colombier 	 */
346219b2ee8SDavid du Colombier 	if (!rmail && mp->size == 0)
3473e12c5d1SDavid du Colombier 		return 0;
3483e12c5d1SDavid du Colombier 
3497dd7cddfSDavid du Colombier 	rfc822cruft(mp);
3507dd7cddfSDavid du Colombier 
3513e12c5d1SDavid du Colombier 	return mp;
3523e12c5d1SDavid du Colombier }
3533e12c5d1SDavid du Colombier 
3543e12c5d1SDavid du Colombier /* return a piece of message starting at `offset' */
3553e12c5d1SDavid du Colombier extern int
m_get(message * mp,long offset,char ** pp)3563e12c5d1SDavid du Colombier m_get(message *mp, long offset, char **pp)
3573e12c5d1SDavid du Colombier {
3583e12c5d1SDavid du Colombier 	static char buf[4*1024];
3593e12c5d1SDavid du Colombier 
3603e12c5d1SDavid du Colombier 	/*
3613e12c5d1SDavid du Colombier 	 *  are we past eof?
3623e12c5d1SDavid du Colombier 	 */
3633e12c5d1SDavid du Colombier 	if(offset >= mp->size)
3643e12c5d1SDavid du Colombier 		return 0;
3653e12c5d1SDavid du Colombier 
3663e12c5d1SDavid du Colombier 	/*
3673e12c5d1SDavid du Colombier 	 *  are we in the virtual memory portion?
3683e12c5d1SDavid du Colombier 	 */
3697dd7cddfSDavid du Colombier 	if(offset < s_len(mp->body)){
3703e12c5d1SDavid du Colombier 		*pp = mp->body->base + offset;
3713e12c5d1SDavid du Colombier 		return mp->body->ptr - mp->body->base - offset;
3723e12c5d1SDavid du Colombier 	}
3733e12c5d1SDavid du Colombier 
3743e12c5d1SDavid du Colombier 	/*
3753e12c5d1SDavid du Colombier 	 *  read it from the temp file
3763e12c5d1SDavid du Colombier 	 */
3777dd7cddfSDavid du Colombier 	offset -= s_len(mp->body);
3783e12c5d1SDavid du Colombier 	if(mp->fd < 0)
3793e12c5d1SDavid du Colombier 		return -1;
3803e12c5d1SDavid du Colombier 	if(seek(mp->fd, offset, 0)<0)
3813e12c5d1SDavid du Colombier 		return -1;
3823e12c5d1SDavid du Colombier 	*pp = buf;
3833e12c5d1SDavid du Colombier 	return read(mp->fd, buf, sizeof buf);
3843e12c5d1SDavid du Colombier }
3853e12c5d1SDavid du Colombier 
3863e12c5d1SDavid du Colombier /* output the message body without ^From escapes */
3873e12c5d1SDavid du Colombier static int
m_noescape(message * mp,Biobuf * fp)3883e12c5d1SDavid du Colombier m_noescape(message *mp, Biobuf *fp)
3893e12c5d1SDavid du Colombier {
3903e12c5d1SDavid du Colombier 	long offset;
3913e12c5d1SDavid du Colombier 	int n;
3923e12c5d1SDavid du Colombier 	char *p;
3933e12c5d1SDavid du Colombier 
3943e12c5d1SDavid du Colombier 	for(offset = 0; offset < mp->size; offset += n){
3953e12c5d1SDavid du Colombier 		n = m_get(mp, offset, &p);
3963e12c5d1SDavid du Colombier 		if(n <= 0){
3973e12c5d1SDavid du Colombier 			Bflush(fp);
3983e12c5d1SDavid du Colombier 			return -1;
3993e12c5d1SDavid du Colombier 		}
4003e12c5d1SDavid du Colombier 		if(Bwrite(fp, p, n) < 0)
4013e12c5d1SDavid du Colombier 			return -1;
4023e12c5d1SDavid du Colombier 	}
4033e12c5d1SDavid du Colombier 	return Bflush(fp);
4043e12c5d1SDavid du Colombier }
4053e12c5d1SDavid du Colombier 
4063e12c5d1SDavid du Colombier /*
407219b2ee8SDavid du Colombier  *  Output the message body with '^From ' escapes.
408219b2ee8SDavid du Colombier  *  Ensures that any line starting with a 'From ' gets a ' ' stuck
4093e12c5d1SDavid du Colombier  *  in front of it.
4103e12c5d1SDavid du Colombier  */
4113e12c5d1SDavid du Colombier static int
m_escape(message * mp,Biobuf * fp)4123e12c5d1SDavid du Colombier m_escape(message *mp, Biobuf *fp)
4133e12c5d1SDavid du Colombier {
414219b2ee8SDavid du Colombier 	char *p, *np;
415219b2ee8SDavid du Colombier 	char *end;
4163e12c5d1SDavid du Colombier 	long offset;
417219b2ee8SDavid du Colombier 	int m, n;
4183e12c5d1SDavid du Colombier 	char *start;
4193e12c5d1SDavid du Colombier 
4203e12c5d1SDavid du Colombier 	for(offset = 0; offset < mp->size; offset += n){
4213e12c5d1SDavid du Colombier 		n = m_get(mp, offset, &start);
4223e12c5d1SDavid du Colombier 		if(n < 0){
4233e12c5d1SDavid du Colombier 			Bflush(fp);
4243e12c5d1SDavid du Colombier 			return -1;
4253e12c5d1SDavid du Colombier 		}
4263e12c5d1SDavid du Colombier 
4273e12c5d1SDavid du Colombier 		p = start;
428219b2ee8SDavid du Colombier 		for(end = p+n; p < end; p += m){
429219b2ee8SDavid du Colombier 			np = memchr(p, '\n', end-p);
430219b2ee8SDavid du Colombier 			if(np == 0){
431219b2ee8SDavid du Colombier 				Bwrite(fp, p, end-p);
4323e12c5d1SDavid du Colombier 				break;
4333e12c5d1SDavid du Colombier 			}
434219b2ee8SDavid du Colombier 			m = np - p + 1;
435219b2ee8SDavid du Colombier 			if(m > 5 && strncmp(p, "From ", 5) == 0)
436219b2ee8SDavid du Colombier 				Bputc(fp, ' ');
437219b2ee8SDavid du Colombier 			Bwrite(fp, p, m);
4383e12c5d1SDavid du Colombier 		}
4393e12c5d1SDavid du Colombier 	}
4403e12c5d1SDavid du Colombier 	Bflush(fp);
4413e12c5d1SDavid du Colombier 	return 0;
4423e12c5d1SDavid du Colombier }
4433e12c5d1SDavid du Colombier 
4447dd7cddfSDavid du Colombier static int
printfrom(message * mp,Biobuf * fp)4457dd7cddfSDavid du Colombier printfrom(message *mp, Biobuf *fp)
4467dd7cddfSDavid du Colombier {
4477dd7cddfSDavid du Colombier 	String *s;
4487dd7cddfSDavid du Colombier 	int rv;
4497dd7cddfSDavid du Colombier 
4507dd7cddfSDavid du Colombier 	if(!returnable(s_to_c(mp->sender)))
4517dd7cddfSDavid du Colombier 		return Bprint(fp, "From: Postmaster\n");
4527dd7cddfSDavid du Colombier 
4537dd7cddfSDavid du Colombier 	s = username(mp->sender);
4547dd7cddfSDavid du Colombier 	if(s) {
4557dd7cddfSDavid du Colombier 		s_append(s, " <");
4567dd7cddfSDavid du Colombier 		s_append(s, s_to_c(mp->sender));
4577dd7cddfSDavid du Colombier 		s_append(s, ">");
4587dd7cddfSDavid du Colombier 	} else {
4597dd7cddfSDavid du Colombier 		s = s_copy(s_to_c(mp->sender));
4607dd7cddfSDavid du Colombier 	}
4617dd7cddfSDavid du Colombier 	s = unescapespecial(s);
4627dd7cddfSDavid du Colombier 	rv = Bprint(fp, "From: %s\n", s_to_c(s));
4637dd7cddfSDavid du Colombier 	s_free(s);
4647dd7cddfSDavid du Colombier 	return rv;
4657dd7cddfSDavid du Colombier }
4667dd7cddfSDavid du Colombier 
4677dd7cddfSDavid du Colombier static char *
rewritezone(char * z)4687dd7cddfSDavid du Colombier rewritezone(char *z)
4697dd7cddfSDavid du Colombier {
4707dd7cddfSDavid du Colombier 	int mindiff;
4717dd7cddfSDavid du Colombier 	char s;
4727dd7cddfSDavid du Colombier 	Tm *tm;
4737dd7cddfSDavid du Colombier 	static char x[7];
4747dd7cddfSDavid du Colombier 
4757dd7cddfSDavid du Colombier 	tm = localtime(time(0));
4767dd7cddfSDavid du Colombier 	mindiff = tm->tzoff/60;
4777dd7cddfSDavid du Colombier 
4787dd7cddfSDavid du Colombier 	/* if not in my timezone, don't change anything */
4797dd7cddfSDavid du Colombier 	if(strcmp(tm->zone, z) != 0)
4807dd7cddfSDavid du Colombier 		return z;
4817dd7cddfSDavid du Colombier 
4827dd7cddfSDavid du Colombier 	if(mindiff < 0){
4837dd7cddfSDavid du Colombier 		s = '-';
4847dd7cddfSDavid du Colombier 		mindiff = -mindiff;
4857dd7cddfSDavid du Colombier 	} else
4867dd7cddfSDavid du Colombier 		s = '+';
4877dd7cddfSDavid du Colombier 
4887dd7cddfSDavid du Colombier 	sprint(x, "%c%.2d%.2d", s, mindiff/60, mindiff%60);
4897dd7cddfSDavid du Colombier 	return x;
4907dd7cddfSDavid du Colombier }
4917dd7cddfSDavid du Colombier 
4927dd7cddfSDavid du Colombier int
isutf8(String * s)4937dd7cddfSDavid du Colombier isutf8(String *s)
4947dd7cddfSDavid du Colombier {
4957dd7cddfSDavid du Colombier 	char *p;
4967dd7cddfSDavid du Colombier 
4977dd7cddfSDavid du Colombier 	for(p = s_to_c(s);  *p; p++)
4987dd7cddfSDavid du Colombier 		if(*p&0x80)
4997dd7cddfSDavid du Colombier 			return 1;
5007dd7cddfSDavid du Colombier 	return 0;
5017dd7cddfSDavid du Colombier }
5027dd7cddfSDavid du Colombier 
5037dd7cddfSDavid du Colombier void
printutf8mime(Biobuf * b)5047dd7cddfSDavid du Colombier printutf8mime(Biobuf *b)
5057dd7cddfSDavid du Colombier {
5067dd7cddfSDavid du Colombier 	Bprint(b, "MIME-Version: 1.0\n");
5077dd7cddfSDavid du Colombier 	Bprint(b, "Content-Type: text/plain; charset=\"UTF-8\"\n");
5087dd7cddfSDavid du Colombier 	Bprint(b, "Content-Transfer-Encoding: 8bit\n");
5097dd7cddfSDavid du Colombier }
5107dd7cddfSDavid du Colombier 
5113e12c5d1SDavid du Colombier /* output a message */
5123e12c5d1SDavid du Colombier extern int
m_print(message * mp,Biobuf * fp,char * remote,int mbox)5133e12c5d1SDavid du Colombier m_print(message *mp, Biobuf *fp, char *remote, int mbox)
5143e12c5d1SDavid du Colombier {
5157dd7cddfSDavid du Colombier 	String *date, *sender;
5167dd7cddfSDavid du Colombier 	char *f[6];
5177dd7cddfSDavid du Colombier 	int n;
5187dd7cddfSDavid du Colombier 
5197dd7cddfSDavid du Colombier 	sender = unescapespecial(s_clone(mp->sender));
5207dd7cddfSDavid du Colombier 
5213e12c5d1SDavid du Colombier 	if (remote != 0){
5227dd7cddfSDavid du Colombier 		if(print_remote_header(fp,s_to_c(sender),s_to_c(mp->date),remote) < 0){
5237dd7cddfSDavid du Colombier 			s_free(sender);
5243e12c5d1SDavid du Colombier 			return -1;
5257dd7cddfSDavid du Colombier 		}
5263e12c5d1SDavid du Colombier 	} else {
5277dd7cddfSDavid du Colombier 		if(print_header(fp, s_to_c(sender), s_to_c(mp->date)) < 0){
5287dd7cddfSDavid du Colombier 			s_free(sender);
5297dd7cddfSDavid du Colombier 			return -1;
5307dd7cddfSDavid du Colombier 		}
5317dd7cddfSDavid du Colombier 	}
5327dd7cddfSDavid du Colombier 	s_free(sender);
5337dd7cddfSDavid du Colombier 	if(!rmail && !mp->havedate){
5347dd7cddfSDavid du Colombier 		/* add a date: line Date: Sun, 19 Apr 1998 12:27:52 -0400 */
5357dd7cddfSDavid du Colombier 		date = s_copy(s_to_c(mp->date));
5367dd7cddfSDavid du Colombier 		n = getfields(s_to_c(date), f, 6, 1, " \t");
5377dd7cddfSDavid du Colombier 		if(n == 6)
5387dd7cddfSDavid du Colombier 			Bprint(fp, "Date: %s, %s %s %s %s %s\n", f[0], f[2], f[1],
5397dd7cddfSDavid du Colombier 			 f[5], f[3], rewritezone(f[4]));
5407dd7cddfSDavid du Colombier 	}
5417dd7cddfSDavid du Colombier 	if(!rmail && !mp->havemime && isutf8(mp->body))
5427dd7cddfSDavid du Colombier 		printutf8mime(fp);
5437dd7cddfSDavid du Colombier 	if(mp->to){
5447dd7cddfSDavid du Colombier 		/* add the to: line */
5457dd7cddfSDavid du Colombier 		if (Bprint(fp, "%s\n", s_to_c(mp->to)) < 0)
5467dd7cddfSDavid du Colombier 			return -1;
5477dd7cddfSDavid du Colombier 		/* add the from: line */
5487dd7cddfSDavid du Colombier 		if (!mp->havefrom && printfrom(mp, fp) < 0)
5497dd7cddfSDavid du Colombier 			return -1;
5507dd7cddfSDavid du Colombier 		if(!mp->rfc822headers && *s_to_c(mp->body) != '\n')
5517dd7cddfSDavid du Colombier 			if (Bprint(fp, "\n") < 0)
5527dd7cddfSDavid du Colombier 				return -1;
5537dd7cddfSDavid du Colombier 	} else if(!rmail){
5547dd7cddfSDavid du Colombier 		/* add the from: line */
5557dd7cddfSDavid du Colombier 		if (!mp->havefrom && printfrom(mp, fp) < 0)
5567dd7cddfSDavid du Colombier 			return -1;
5577dd7cddfSDavid du Colombier 		if(!mp->rfc822headers && *s_to_c(mp->body) != '\n')
5587dd7cddfSDavid du Colombier 			if (Bprint(fp, "\n") < 0)
5593e12c5d1SDavid du Colombier 				return -1;
5603e12c5d1SDavid du Colombier 	}
5613e12c5d1SDavid du Colombier 
5623e12c5d1SDavid du Colombier 	if (!mbox)
5633e12c5d1SDavid du Colombier 		return m_noescape(mp, fp);
5643e12c5d1SDavid du Colombier 	return m_escape(mp, fp);
5653e12c5d1SDavid du Colombier }
5663e12c5d1SDavid du Colombier 
5673e12c5d1SDavid du Colombier /* print just the message body */
5683e12c5d1SDavid du Colombier extern int
m_bprint(message * mp,Biobuf * fp)5693e12c5d1SDavid du Colombier m_bprint(message *mp, Biobuf *fp)
5703e12c5d1SDavid du Colombier {
5713e12c5d1SDavid du Colombier 	return m_noescape(mp, fp);
5723e12c5d1SDavid du Colombier }
573