1 #include "common.h"
2 #include "send.h"
3 
4 #include "../smtp/smtp.h"
5 #include "../smtp/rfc822.tab.h"
6 
7 /* global to this file */
8 static Reprog *rfprog;
9 static Reprog *fprog;
10 
11 #define VMLIMIT (64*1024)
12 #define MSGLIMIT (128*1024*1024)
13 
14 int received;	/* from rfc822.y */
15 
16 static String*	getstring(Node *p);
17 static String*	getaddr(Node *p);
18 
19 extern int
default_from(message * mp)20 default_from(message *mp)
21 {
22 	char *cp, *lp;
23 
24 	cp = getenv("upasname");
25 	lp = getlog();
26 	if(lp == nil)
27 		return -1;
28 
29 	if(cp && *cp)
30 		s_append(mp->sender, cp);
31 	else
32 		s_append(mp->sender, lp);
33 	s_append(mp->date, thedate());
34 	return 0;
35 }
36 
37 extern message *
m_new(void)38 m_new(void)
39 {
40 	message *mp;
41 
42 	mp = (message *)mallocz(sizeof(message), 1);
43 	if (mp == 0) {
44 		perror("message:");
45 		exit(1);
46 	}
47 	mp->sender = s_new();
48 	mp->replyaddr = s_new();
49 	mp->date = s_new();
50 	mp->body = s_new();
51 	mp->size = 0;
52 	mp->fd = -1;
53 	return mp;
54 }
55 
56 extern void
m_free(message * mp)57 m_free(message *mp)
58 {
59 	if(mp->fd >= 0){
60 		close(mp->fd);
61 		sysremove(s_to_c(mp->tmp));
62 		s_free(mp->tmp);
63 	}
64 	s_free(mp->sender);
65 	s_free(mp->date);
66 	s_free(mp->body);
67 	s_free(mp->havefrom);
68 	s_free(mp->havesender);
69 	s_free(mp->havereplyto);
70 	s_free(mp->havesubject);
71 	free((char *)mp);
72 }
73 
74 /* read a message into a temp file , return an open fd to it */
75 static int
m_read_to_file(Biobuf * fp,message * mp)76 m_read_to_file(Biobuf *fp, message *mp)
77 {
78 	int fd;
79 	int n;
80 	String *file;
81 	char buf[4*1024];
82 
83 	file = s_new();
84 	/*
85 	 *  create temp file to be remove on close
86 	 */
87 	abspath("mtXXXXXX", UPASTMP, file);
88 	mktemp(s_to_c(file));
89 	if((fd = syscreate(s_to_c(file), ORDWR|ORCLOSE, 0600))<0){
90 		s_free(file);
91 		return -1;
92 	}
93 	mp->tmp = file;
94 
95 	/*
96 	 *  read the rest into the temp file
97 	 */
98 	while((n = Bread(fp, buf, sizeof(buf))) > 0){
99 		if(write(fd, buf, n) != n){
100 			close(fd);
101 			return -1;
102 		}
103 		mp->size += n;
104 		if(mp->size > MSGLIMIT){
105 			mp->size = -1;
106 			break;
107 		}
108 	}
109 
110 	mp->fd = fd;
111 	return 0;
112 }
113 
114 /* get the first address from a node */
115 static String*
getaddr(Node * p)116 getaddr(Node *p)
117 {
118 	for(; p; p = p->next)
119 		if(p->s && p->addr)
120 			return s_copy(s_to_c(p->s));
121 	return nil;
122 }
123 
124 /* get the text of a header line minus the field name */
125 static String*
getstring(Node * p)126 getstring(Node *p)
127 {
128 	String *s;
129 
130 	s = s_new();
131 	if(p == nil)
132 		return s;
133 
134 	for(p = p->next; p; p = p->next){
135 		if(p->s){
136 			s_append(s, s_to_c(p->s));
137 		}else{
138 			s_putc(s, p->c);
139 			s_terminate(s);
140 		}
141 		if(p->white)
142 			s_append(s, s_to_c(p->white));
143 	}
144 	return s;
145 }
146 
147 #if 0
148 static char *fieldname[] =
149 {
150 [WORD-WORD]	"WORD",
151 [DATE-WORD]	"DATE",
152 [RESENT_DATE-WORD]	"RESENT_DATE",
153 [RETURN_PATH-WORD]	"RETURN_PATH",
154 [FROM-WORD]	"FROM",
155 [SENDER-WORD]	"SENDER",
156 [REPLY_TO-WORD]	"REPLY_TO",
157 [RESENT_FROM-WORD]	"RESENT_FROM",
158 [RESENT_SENDER-WORD]	"RESENT_SENDER",
159 [RESENT_REPLY_TO-WORD]	"RESENT_REPLY_TO",
160 [SUBJECT-WORD]	"SUBJECT",
161 [TO-WORD]	"TO",
162 [CC-WORD]	"CC",
163 [BCC-WORD]	"BCC",
164 [RESENT_TO-WORD]	"RESENT_TO",
165 [RESENT_CC-WORD]	"RESENT_CC",
166 [RESENT_BCC-WORD]	"RESENT_BCC",
167 [REMOTE-WORD]	"REMOTE",
168 [PRECEDENCE-WORD]	"PRECEDENCE",
169 [MIMEVERSION-WORD]	"MIMEVERSION",
170 [CONTENTTYPE-WORD]	"CONTENTTYPE",
171 [MESSAGEID-WORD]	"MESSAGEID",
172 [RECEIVED-WORD]	"RECEIVED",
173 [MAILER-WORD]	"MAILER",
174 [BADTOKEN-WORD]	"BADTOKEN"
175 };
176 #endif
177 
178 /* fix 822 addresses */
179 static void
rfc822cruft(message * mp)180 rfc822cruft(message *mp)
181 {
182 	Field *f;
183 	Node *p;
184 	String *body, *s;
185 	char *cp;
186 
187 	/*
188 	 *  parse headers in in-core part
189 	 */
190 	yyinit(s_to_c(mp->body), s_len(mp->body));
191 	mp->rfc822headers = 0;
192 	yyparse();
193 	mp->rfc822headers = 1;
194 	mp->received = received;
195 
196 	/*
197 	 *  remove equivalent systems in all addresses
198 	 */
199 	body = s_new();
200 	cp = s_to_c(mp->body);
201 	for(f = firstfield; f; f = f->next){
202 		if(f->node->c == MIMEVERSION)
203 			mp->havemime = 1;
204 		if(f->node->c == FROM)
205 			mp->havefrom = getaddr(f->node);
206 		if(f->node->c == SENDER)
207 			mp->havesender = getaddr(f->node);
208 		if(f->node->c == REPLY_TO)
209 			mp->havereplyto = getaddr(f->node);
210 		if(f->node->c == TO)
211 			mp->haveto = 1;
212 		if(f->node->c == DATE)
213 			mp->havedate = 1;
214 		if(f->node->c == SUBJECT)
215 			mp->havesubject = getstring(f->node);
216 		if(f->node->c == PRECEDENCE && f->node->next && f->node->next->next){
217 			s = f->node->next->next->s;
218 			if(s && (strcmp(s_to_c(s), "bulk") == 0
219 				|| strcmp(s_to_c(s), "Bulk") == 0))
220 					mp->bulk = 1;
221 		}
222 		for(p = f->node; p; p = p->next){
223 			if(p->s){
224 				if(p->addr){
225 					cp = skipequiv(s_to_c(p->s));
226 					s_append(body, cp);
227 				} else
228 					s_append(body, s_to_c(p->s));
229 			}else{
230 				s_putc(body, p->c);
231 				s_terminate(body);
232 			}
233 			if(p->white)
234 				s_append(body, s_to_c(p->white));
235 			cp = p->end+1;
236 		}
237 		s_append(body, "\n");
238 	}
239 
240 	if(*s_to_c(body) == 0){
241 		s_free(body);
242 		return;
243 	}
244 
245 	if(*cp != '\n')
246 		s_append(body, "\n");
247 	s_memappend(body, cp, s_len(mp->body) - (cp - s_to_c(mp->body)));
248 	s_terminate(body);
249 
250 	firstfield = 0;
251 	mp->size += s_len(body) - s_len(mp->body);
252 	s_free(mp->body);
253 	mp->body = body;
254 }
255 
256 /* read in a message, interpret the 'From' header */
257 extern message *
m_read(Biobuf * fp,int rmail,int interactive)258 m_read(Biobuf *fp, int rmail, int interactive)
259 {
260 	message *mp;
261 	Resub subexp[10];
262 	char *line;
263 	int first;
264 	int n;
265 
266 	mp = m_new();
267 
268 	/* parse From lines if remote */
269 	if (rmail) {
270 		/* get remote address */
271 		String *sender=s_new();
272 
273 		if (rfprog == 0)
274 			rfprog = regcomp(REMFROMRE);
275 		first = 1;
276 		while(s_read_line(fp, s_restart(mp->body)) != 0) {
277 			memset(subexp, 0, sizeof(subexp));
278 			if (regexec(rfprog, s_to_c(mp->body), subexp, 10) == 0){
279 				if(first == 0)
280 					break;
281 				if (fprog == 0)
282 					fprog = regcomp(FROMRE);
283 				memset(subexp, 0, sizeof(subexp));
284 				if(regexec(fprog, s_to_c(mp->body), subexp,10) == 0)
285 					break;
286 				s_restart(mp->body);
287 				append_match(subexp, s_restart(sender), SENDERMATCH);
288 				append_match(subexp, s_restart(mp->date), DATEMATCH);
289 				break;
290 			}
291 			append_match(subexp, s_restart(sender), REMSENDERMATCH);
292 			append_match(subexp, s_restart(mp->date), REMDATEMATCH);
293 			if(subexp[REMSYSMATCH].s.sp!=subexp[REMSYSMATCH].e.ep){
294 				append_match(subexp, mp->sender, REMSYSMATCH);
295 				s_append(mp->sender, "!");
296 			}
297 			first = 0;
298 		}
299 		s_append(mp->sender, s_to_c(sender));
300 
301 		s_free(sender);
302 	}
303 	if(*s_to_c(mp->sender)=='\0')
304 		default_from(mp);
305 
306 	/* if sender address is unreturnable, treat message as bulk mail */
307 	if(!returnable(s_to_c(mp->sender)))
308 		mp->bulk = 1;
309 
310 	/* get body */
311 	if(interactive && !rmail){
312 		/* user typing on terminal: terminator == '.' or EOF */
313 		for(;;) {
314 			line = s_read_line(fp, mp->body);
315 			if (line == 0)
316 				break;
317 			if (strcmp(".\n", line)==0) {
318 				mp->body->ptr -= 2;
319 				*mp->body->ptr = '\0';
320 				break;
321 			}
322 		}
323 		mp->size = mp->body->ptr - mp->body->base;
324 	} else {
325 		/*
326 		 *  read up to VMLIMIT bytes (more or less) into main memory.
327 		 *  if message is longer put the rest in a tmp file.
328 		 */
329 		mp->size = mp->body->ptr - mp->body->base;
330 		n = s_read(fp, mp->body, VMLIMIT);
331 		if(n < 0){
332 			perror("m_read");
333 			exit(1);
334 		}
335 		mp->size += n;
336 		if(n == VMLIMIT){
337 			if(m_read_to_file(fp, mp) < 0){
338 				perror("m_read_to_file");
339 				exit(1);
340 			}
341 		}
342 	}
343 
344 	/*
345 	 *  ignore 0 length messages from a terminal
346 	 */
347 	if (!rmail && mp->size == 0)
348 		return 0;
349 
350 	rfc822cruft(mp);
351 
352 	return mp;
353 }
354 
355 /* return a piece of message starting at `offset' */
356 extern int
m_get(message * mp,long offset,char ** pp)357 m_get(message *mp, long offset, char **pp)
358 {
359 	static char buf[4*1024];
360 
361 	/*
362 	 *  are we past eof?
363 	 */
364 	if(offset >= mp->size)
365 		return 0;
366 
367 	/*
368 	 *  are we in the virtual memory portion?
369 	 */
370 	if(offset < s_len(mp->body)){
371 		*pp = mp->body->base + offset;
372 		return mp->body->ptr - mp->body->base - offset;
373 	}
374 
375 	/*
376 	 *  read it from the temp file
377 	 */
378 	offset -= s_len(mp->body);
379 	if(mp->fd < 0)
380 		return -1;
381 	if(seek(mp->fd, offset, 0)<0)
382 		return -1;
383 	*pp = buf;
384 	return read(mp->fd, buf, sizeof buf);
385 }
386 
387 /* output the message body without ^From escapes */
388 static int
m_noescape(message * mp,Biobuf * fp)389 m_noescape(message *mp, Biobuf *fp)
390 {
391 	long offset;
392 	int n;
393 	char *p;
394 
395 	for(offset = 0; offset < mp->size; offset += n){
396 		n = m_get(mp, offset, &p);
397 		if(n <= 0){
398 			Bflush(fp);
399 			return -1;
400 		}
401 		if(Bwrite(fp, p, n) < 0)
402 			return -1;
403 	}
404 	return Bflush(fp);
405 }
406 
407 /*
408  *  Output the message body with '^From ' escapes.
409  *  Ensures that any line starting with a 'From ' gets a ' ' stuck
410  *  in front of it.
411  */
412 static int
m_escape(message * mp,Biobuf * fp)413 m_escape(message *mp, Biobuf *fp)
414 {
415 	char *p, *np;
416 	char *end;
417 	long offset;
418 	int m, n;
419 	char *start;
420 
421 	for(offset = 0; offset < mp->size; offset += n){
422 		n = m_get(mp, offset, &start);
423 		if(n < 0){
424 			Bflush(fp);
425 			return -1;
426 		}
427 
428 		p = start;
429 		for(end = p+n; p < end; p += m){
430 			np = memchr(p, '\n', end-p);
431 			if(np == 0){
432 				Bwrite(fp, p, end-p);
433 				break;
434 			}
435 			m = np - p + 1;
436 			if(m > 5 && strncmp(p, "From ", 5) == 0)
437 				Bputc(fp, ' ');
438 			Bwrite(fp, p, m);
439 		}
440 	}
441 	Bflush(fp);
442 	return 0;
443 }
444 
445 static int
printfrom(message * mp,Biobuf * fp)446 printfrom(message *mp, Biobuf *fp)
447 {
448 	String *s;
449 	int rv;
450 
451 	if(!returnable(s_to_c(mp->sender)))
452 		return Bprint(fp, "From: Postmaster\n");
453 
454 	s = username(mp->sender);
455 	if(s) {
456 		s_append(s, " <");
457 		s_append(s, s_to_c(mp->sender));
458 		s_append(s, ">");
459 	} else {
460 		s = s_copy(s_to_c(mp->sender));
461 	}
462 	s = unescapespecial(s);
463 	rv = Bprint(fp, "From: %s\n", s_to_c(s));
464 	s_free(s);
465 	return rv;
466 }
467 
468 static char *
rewritezone(char * z)469 rewritezone(char *z)
470 {
471 	int mindiff;
472 	char s;
473 	Tm *tm;
474 	static char x[7];
475 
476 	tm = localtime(time(0));
477 	mindiff = tm->tzoff/60;
478 
479 	/* if not in my timezone, don't change anything */
480 	if(strcmp(tm->zone, z) != 0)
481 		return z;
482 
483 	if(mindiff < 0){
484 		s = '-';
485 		mindiff = -mindiff;
486 	} else
487 		s = '+';
488 
489 	sprint(x, "%c%.2d%.2d", s, mindiff/60, mindiff%60);
490 	return x;
491 }
492 
493 int
isutf8(String * s)494 isutf8(String *s)
495 {
496 	char *p;
497 
498 	for(p = s_to_c(s);  *p; p++)
499 		if(*p&0x80)
500 			return 1;
501 	return 0;
502 }
503 
504 void
printutf8mime(Biobuf * b)505 printutf8mime(Biobuf *b)
506 {
507 	Bprint(b, "MIME-Version: 1.0\n");
508 	Bprint(b, "Content-Type: text/plain; charset=\"UTF-8\"\n");
509 	Bprint(b, "Content-Transfer-Encoding: 8bit\n");
510 }
511 
512 /* output a message */
513 extern int
m_print(message * mp,Biobuf * fp,char * remote,int mbox)514 m_print(message *mp, Biobuf *fp, char *remote, int mbox)
515 {
516 	String *date, *sender;
517 	char *f[6];
518 	int n;
519 
520 	sender = unescapespecial(s_clone(mp->sender));
521 
522 	if (remote != 0){
523 		if(print_remote_header(fp,s_to_c(sender),s_to_c(mp->date),remote) < 0){
524 			s_free(sender);
525 			return -1;
526 		}
527 	} else {
528 		if(print_header(fp, s_to_c(sender), s_to_c(mp->date)) < 0){
529 			s_free(sender);
530 			return -1;
531 		}
532 	}
533 	s_free(sender);
534 	if(!rmail && !mp->havedate){
535 		/* add a date: line Date: Sun, 19 Apr 1998 12:27:52 -0400 */
536 		date = s_copy(s_to_c(mp->date));
537 		n = getfields(s_to_c(date), f, 6, 1, " \t");
538 		if(n == 6)
539 			Bprint(fp, "Date: %s, %s %s %s %s %s\n", f[0], f[2], f[1],
540 			 f[5], f[3], rewritezone(f[4]));
541 	}
542 	if(!rmail && !mp->havemime && isutf8(mp->body))
543 		printutf8mime(fp);
544 	if(mp->to){
545 		/* add the to: line */
546 		if (Bprint(fp, "%s\n", s_to_c(mp->to)) < 0)
547 			return -1;
548 		/* add the from: line */
549 		if (!mp->havefrom && printfrom(mp, fp) < 0)
550 			return -1;
551 		if(!mp->rfc822headers && *s_to_c(mp->body) != '\n')
552 			if (Bprint(fp, "\n") < 0)
553 				return -1;
554 	} else if(!rmail){
555 		/* add the from: line */
556 		if (!mp->havefrom && printfrom(mp, fp) < 0)
557 			return -1;
558 		if(!mp->rfc822headers && *s_to_c(mp->body) != '\n')
559 			if (Bprint(fp, "\n") < 0)
560 				return -1;
561 	}
562 
563 	if (!mbox)
564 		return m_noescape(mp, fp);
565 	return m_escape(mp, fp);
566 }
567 
568 /* print just the message body */
569 extern int
m_bprint(message * mp,Biobuf * fp)570 m_bprint(message *mp, Biobuf *fp)
571 {
572 	return m_noescape(mp, fp);
573 }
574