Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

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