Warning: Attempt to read property "date" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "msg" on null in /usr/local/www/websvn.planix.org/blame.php on line 247
WebSVN – planix.SVN – Blame – /os/branches/feature_tlsv12/sys/src/cmd/upas/fs/mbox.c – Rev 2

Subversion Repositories planix.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include "common.h"
2
#include <ctype.h>
3
#include <plumb.h>
4
#include <libsec.h>
5
#include "dat.h"
6
 
7
typedef struct Header Header;
8
 
9
struct Header {
10
	char *type;
11
	void (*f)(Message*, Header*, char*);
12
	int len;
13
};
14
 
15
/* headers */
16
static	void	ctype(Message*, Header*, char*);
17
static	void	cencoding(Message*, Header*, char*);
18
static	void	cdisposition(Message*, Header*, char*);
19
static	void	date822(Message*, Header*, char*);
20
static	void	from822(Message*, Header*, char*);
21
static	void	to822(Message*, Header*, char*);
22
static	void	sender822(Message*, Header*, char*);
23
static	void	replyto822(Message*, Header*, char*);
24
static	void	subject822(Message*, Header*, char*);
25
static	void	inreplyto822(Message*, Header*, char*);
26
static	void	cc822(Message*, Header*, char*);
27
static	void	bcc822(Message*, Header*, char*);
28
static	void	messageid822(Message*, Header*, char*);
29
static	void	mimeversion(Message*, Header*, char*);
30
static	void	nullsqueeze(Message*);
31
enum
32
{
33
	Mhead=	11,	/* offset of first mime header */
34
};
35
 
36
Header head[] =
37
{
38
	{ "date:", date822, },
39
	{ "from:", from822, },
40
	{ "to:", to822, },
41
	{ "sender:", sender822, },
42
	{ "reply-to:", replyto822, },
43
	{ "subject:", subject822, },
44
	{ "cc:", cc822, },
45
	{ "bcc:", bcc822, },
46
	{ "in-reply-to:", inreplyto822, },
47
	{ "mime-version:", mimeversion, },
48
	{ "message-id:", messageid822, },
49
 
50
[Mhead]	{ "content-type:", ctype, },
51
	{ "content-transfer-encoding:", cencoding, },
52
	{ "content-disposition:", cdisposition, },
53
	{ 0, },
54
};
55
 
56
static	void	fatal(char *fmt, ...);
57
static	void	initquoted(void);
58
static	void	startheader(Message*);
59
static	void	startbody(Message*);
60
static	char*	skipwhite(char*);
61
static	char*	skiptosemi(char*);
62
static	char*	getstring(char*, String*, int);
63
static	void	setfilename(Message*, char*);
64
static	char*	lowercase(char*);
65
static	int	is8bit(Message*);
66
static	int	headerline(char**, String*);
67
static	void	initheaders(void);
68
static void	parseattachments(Message*, Mailbox*);
69
 
70
int		debug;
71
 
72
char *Enotme = "path not served by this file server";
73
 
74
enum
75
{
76
	Chunksize = 1024,
77
};
78
 
79
Mailboxinit *boxinit[] = {
80
	imap4mbox,
81
	pop3mbox,
82
	planbmbox,
83
	planbvmbox,
84
	plan9mbox,
85
};
86
 
87
char*
88
syncmbox(Mailbox *mb, int doplumb)
89
{
90
	return (*mb->sync)(mb, doplumb);
91
}
92
 
93
/* create a new mailbox */
94
char*
95
newmbox(char *path, char *name, int std)
96
{
97
	Mailbox *mb, **l;
98
	char *p, *rv;
99
	int i;
100
 
101
	initheaders();
102
 
103
	mb = emalloc(sizeof(*mb));
104
	strncpy(mb->path, path, sizeof(mb->path)-1);
105
	if(name == nil){
106
		p = strrchr(path, '/');
107
		if(p == nil)
108
			p = path;
109
		else
110
			p++;
111
		if(*p == 0){
112
			free(mb);
113
			return "bad mbox name";
114
		}
115
		strncpy(mb->name, p, sizeof(mb->name)-1);
116
	} else {
117
		strncpy(mb->name, name, sizeof(mb->name)-1);
118
	}
119
 
120
	rv = nil;
121
	// check for a mailbox type
122
	for(i=0; i<nelem(boxinit); i++)
123
		if((rv = (*boxinit[i])(mb, path)) != Enotme)
124
			break;
125
	if(i == nelem(boxinit)){
126
		free(mb);
127
		return "bad path";
128
	}
129
 
130
	// on error, give up
131
	if(rv){
132
		free(mb);
133
		return rv;
134
	}
135
 
136
	// make sure name isn't taken
137
	qlock(&mbllock);
138
	for(l = &mbl; *l != nil; l = &(*l)->next){
139
		if(strcmp((*l)->name, mb->name) == 0){
140
			if(strcmp(path, (*l)->path) == 0)
141
				rv = nil;
142
			else
143
				rv = "mbox name in use";
144
			if(mb->close)
145
				(*mb->close)(mb);
146
			free(mb);
147
			qunlock(&mbllock);
148
			return rv;
149
		}
150
	}
151
 
152
	// always try locking
153
	mb->dolock = 1;
154
 
155
	mb->refs = 1;
156
	mb->next = nil;
157
	mb->id = newid();
158
	mb->root = newmessage(nil);
159
	mb->std = std;
160
	*l = mb;
161
	qunlock(&mbllock);
162
 
163
	qlock(mb);
164
	if(mb->ctl){
165
		henter(PATH(mb->id, Qmbox), "ctl",
166
			(Qid){PATH(mb->id, Qmboxctl), 0, QTFILE}, nil, mb);
167
	}
168
	rv = syncmbox(mb, 0);
169
	qunlock(mb);
170
 
171
	return rv;
172
}
173
 
174
// close the named mailbox
175
void
176
freembox(char *name)
177
{
178
	Mailbox **l, *mb;
179
 
180
	qlock(&mbllock);
181
	for(l=&mbl; *l != nil; l=&(*l)->next){
182
		if(strcmp(name, (*l)->name) == 0){
183
			mb = *l;
184
			*l = mb->next;
185
			mboxdecref(mb);
186
			break;
187
		}
188
	}
189
	hfree(PATH(0, Qtop), name);
190
	qunlock(&mbllock);
191
}
192
 
193
static void
194
initheaders(void)
195
{
196
	Header *h;
197
	static int already;
198
 
199
	if(already)
200
		return;
201
	already = 1;
202
 
203
	for(h = head; h->type != nil; h++)
204
		h->len = strlen(h->type);
205
}
206
 
207
/*
208
 *  parse a Unix style header
209
 */
210
void
211
parseunix(Message *m)
212
{
213
	char *p;
214
	String *h;
215
 
216
	h = s_new();
217
	for(p = m->start + 5; *p && *p != '\r' && *p != '\n'; p++)
218
		s_putc(h, *p);
219
	s_terminate(h);
220
	s_restart(h);
221
 
222
	m->unixfrom = s_parse(h, s_reset(m->unixfrom));
223
	m->unixdate = s_append(s_reset(m->unixdate), h->ptr);
224
 
225
	s_free(h);
226
}
227
 
228
/*
229
 *  parse a message
230
 */
231
void
232
parseheaders(Message *m, int justmime, Mailbox *mb, int addfrom)
233
{
234
	String *hl;
235
	Header *h;
236
	char *p, *q;
237
	int i;
238
 
239
	if(m->whole == m->whole->whole){
240
		henter(PATH(mb->id, Qmbox), m->name,
241
			(Qid){PATH(m->id, Qdir), 0, QTDIR}, m, mb);
242
	} else {
243
		henter(PATH(m->whole->id, Qdir), m->name,
244
			(Qid){PATH(m->id, Qdir), 0, QTDIR}, m, mb);
245
	}
246
	for(i = 0; i < Qmax; i++)
247
		henter(PATH(m->id, Qdir), dirtab[i],
248
			(Qid){PATH(m->id, i), 0, QTFILE}, m, mb);
249
 
250
	// parse mime headers
251
	p = m->header;
252
	hl = s_new();
253
	while(headerline(&p, hl)){
254
		if(justmime)
255
			h = &head[Mhead];
256
		else
257
			h = head;
258
		for(; h->type; h++){
259
			if(cistrncmp(s_to_c(hl), h->type, h->len) == 0){
260
				(*h->f)(m, h, s_to_c(hl));
261
				break;
262
			}
263
		}
264
		s_reset(hl);
265
	}
266
	s_free(hl);
267
 
268
	// the blank line isn't really part of the body or header
269
	if(justmime){
270
		m->mhend = p;
271
		m->hend = m->header;
272
	} else {
273
		m->hend = p;
274
	}
275
	if(*p == '\n')
276
		p++;
277
	m->rbody = m->body = p;
278
 
279
	// if type is text, get any nulls out of the body.  This is
280
	// for the two seans and imap clients that get confused.
281
	if(strncmp(s_to_c(m->type), "text/", 5) == 0)
282
		nullsqueeze(m);
283
 
284
	//
285
	// cobble together Unix-style from line
286
	// for local mailbox messages, we end up recreating the
287
	// original header.
288
	// for pop3 messages, the best we can do is 
289
	// use the From: information and the RFC822 date.
290
	//
291
	if(m->unixdate == nil || strcmp(s_to_c(m->unixdate), "???") == 0
292
	|| strcmp(s_to_c(m->unixdate), "Thu Jan 1 00:00:00 GMT 1970") == 0){
293
		if(m->unixdate){
294
			s_free(m->unixdate);
295
			m->unixdate = nil;
296
		}
297
		// look for the date in the first Received: line.
298
		// it's likely to be the right time zone (it's
299
	 	// the local system) and in a convenient format.
300
		if(cistrncmp(m->header, "received:", 9)==0){
301
			if((q = strchr(m->header, ';')) != nil){
302
				p = q;
303
				while((p = strchr(p, '\n')) != nil){
304
					if(p[1] != ' ' && p[1] != '\t' && p[1] != '\n')
305
						break;
306
					p++;
307
				}
308
				if(p){
309
					*p = '\0';
310
					m->unixdate = date822tounix(q+1);
311
					*p = '\n';
312
				}
313
			}
314
		}
315
 
316
		// fall back on the rfc822 date	
317
		if(m->unixdate==nil && m->date822)
318
			m->unixdate = date822tounix(s_to_c(m->date822));
319
	}
320
 
321
	if(m->unixheader != nil)
322
		s_free(m->unixheader);
323
 
324
	// only fake header for top-level messages for pop3 and imap4
325
	// clients (those protocols don't include the unix header).
326
	// adding the unix header all the time screws up mime-attached
327
	// rfc822 messages.
328
	if(!addfrom && !m->unixfrom){
329
		m->unixheader = nil;
330
		return;
331
	}
332
 
333
	m->unixheader = s_copy("From ");
334
	if(m->unixfrom && strcmp(s_to_c(m->unixfrom), "???") != 0)
335
		s_append(m->unixheader, s_to_c(m->unixfrom));
336
	else if(m->from822)
337
		s_append(m->unixheader, s_to_c(m->from822));
338
	else
339
		s_append(m->unixheader, "???");
340
 
341
	s_append(m->unixheader, " ");
342
	if(m->unixdate)
343
		s_append(m->unixheader, s_to_c(m->unixdate));
344
	else
345
		s_append(m->unixheader, "Thu Jan  1 00:00:00 GMT 1970");
346
 
347
	s_append(m->unixheader, "\n");
348
}
349
 
350
String*
351
promote(String **sp)
352
{
353
	String *s;
354
 
355
	if(*sp != nil)
356
		s = s_clone(*sp);
357
	else
358
		s = nil;
359
	return s;
360
}
361
 
362
void
363
parsebody(Message *m, Mailbox *mb)
364
{
365
	Message *nm;
366
 
367
	// recurse
368
	if(strncmp(s_to_c(m->type), "multipart/", 10) == 0){
369
		parseattachments(m, mb);
370
	} else if(strcmp(s_to_c(m->type), "message/rfc822") == 0){
371
		decode(m);
372
		parseattachments(m, mb);
373
		nm = m->part;
374
 
375
		// promote headers
376
		if(m->replyto822 == nil && m->from822 == nil && m->sender822 == nil){
377
			m->from822 = promote(&nm->from822);
378
			m->to822 = promote(&nm->to822);
379
			m->date822 = promote(&nm->date822);
380
			m->sender822 = promote(&nm->sender822);
381
			m->replyto822 = promote(&nm->replyto822);
382
			m->subject822 = promote(&nm->subject822);
383
			m->unixdate = promote(&nm->unixdate);
384
		}
385
	}
386
}
387
 
388
void
389
parse(Message *m, int justmime, Mailbox *mb, int addfrom)
390
{
391
	parseheaders(m, justmime, mb, addfrom);
392
	parsebody(m, mb);
393
}
394
 
395
static void
396
parseattachments(Message *m, Mailbox *mb)
397
{
398
	Message *nm, **l;
399
	char *p, *x;
400
 
401
	// if there's a boundary, recurse...
402
	if(m->boundary != nil){
403
		p = m->body;
404
		nm = nil;
405
		l = &m->part;
406
		for(;;){
407
			x = strstr(p, s_to_c(m->boundary));
408
 
409
			/* no boundary, we're done */
410
			if(x == nil){
411
				if(nm != nil)
412
					nm->rbend = nm->bend = nm->end = m->bend;
413
				break;
414
			}
415
 
416
			/* boundary must be at the start of a line */
417
			if(x != m->body && *(x-1) != '\n'){
418
				p = x+1;
419
				continue;
420
			}
421
 
422
			if(nm != nil)
423
				nm->rbend = nm->bend = nm->end = x;
424
			x += strlen(s_to_c(m->boundary));
425
 
426
			/* is this the last part? ignore anything after it */
427
			if(strncmp(x, "--", 2) == 0)
428
				break;
429
 
430
			p = strchr(x, '\n');
431
			if(p == nil)
432
				break;
433
			nm = newmessage(m);
434
			nm->start = nm->header = nm->body = nm->rbody = ++p;
435
			nm->mheader = nm->header;
436
			*l = nm;
437
			l = &nm->next;
438
		}
439
		for(nm = m->part; nm != nil; nm = nm->next)
440
			parse(nm, 1, mb, 0);
441
		return;
442
	}
443
 
444
	// if we've got an rfc822 message, recurse...
445
	if(strcmp(s_to_c(m->type), "message/rfc822") == 0){
446
		nm = newmessage(m);
447
		m->part = nm;
448
		nm->start = nm->header = nm->body = nm->rbody = m->body;
449
		nm->end = nm->bend = nm->rbend = m->bend;
450
		parse(nm, 0, mb, 0);
451
	}
452
}
453
 
454
/*
455
 *  pick up a header line
456
 */
457
static int
458
headerline(char **pp, String *hl)
459
{
460
	char *p, *x;
461
 
462
	s_reset(hl);
463
	p = *pp;
464
	x = strpbrk(p, ":\n");
465
	if(x == nil || *x == '\n')
466
		return 0;
467
	for(;;){
468
		x = strchr(p, '\n');
469
		if(x == nil)
470
			x = p + strlen(p);
471
		s_nappend(hl, p, x-p);
472
		p = x;
473
		if(*p != '\n' || *++p != ' ' && *p != '\t')
474
			break;
475
		while(*p == ' ' || *p == '\t')
476
			p++;
477
		s_putc(hl, ' ');
478
	}
479
	*pp = p;
480
	return 1;
481
}
482
 
483
/* returns nil iff there are no addressees */
484
static String*
485
addr822(char *p)
486
{
487
	String *s, *list;
488
	int incomment, addrdone, inanticomment, quoted;
489
	int n;
490
	int c;
491
 
492
	list = s_new();
493
	s = s_new();
494
	quoted = incomment = addrdone = inanticomment = 0;
495
	n = 0;
496
	for(; *p; p++){
497
		c = *p;
498
 
499
		// whitespace is ignored
500
		if(!quoted && isspace(c) || c == '\r')
501
			continue;
502
 
503
		// strings are always treated as atoms
504
		if(!quoted && c == '"'){
505
			if(!addrdone && !incomment)
506
				s_putc(s, c);
507
			for(p++; *p; p++){
508
				if(!addrdone && !incomment)
509
					s_putc(s, *p);
510
				if(!quoted && *p == '"')
511
					break;
512
				if(*p == '\\')
513
					quoted = 1;
514
				else
515
					quoted = 0;
516
			}
517
			if(*p == 0)
518
				break;
519
			quoted = 0;
520
			continue;
521
		}
522
 
523
		// ignore everything in an expicit comment
524
		if(!quoted && c == '('){
525
			incomment = 1;
526
			continue;
527
		}
528
		if(incomment){
529
			if(!quoted && c == ')')
530
				incomment = 0;
531
			quoted = 0;
532
			continue;
533
		}
534
 
535
		// anticomments makes everything outside of them comments
536
		if(!quoted && c == '<' && !inanticomment){
537
			inanticomment = 1;
538
			s = s_reset(s);
539
			continue;
540
		}
541
		if(!quoted && c == '>' && inanticomment){
542
			addrdone = 1;
543
			inanticomment = 0;
544
			continue;
545
		}
546
 
547
		// commas separate addresses
548
		if(!quoted && c == ',' && !inanticomment){
549
			s_terminate(s);
550
			addrdone = 0;
551
			if(n++ != 0)
552
				s_append(list, " ");
553
			s_append(list, s_to_c(s));
554
			s = s_reset(s);
555
			continue;
556
		}
557
 
558
		// what's left is part of the address
559
		s_putc(s, c);
560
 
561
		// quoted characters are recognized only as characters
562
		if(c == '\\')
563
			quoted = 1;
564
		else
565
			quoted = 0;
566
 
567
	}
568
 
569
	if(*s_to_c(s) != 0){
570
		s_terminate(s);
571
		if(n++ != 0)
572
			s_append(list, " ");
573
		s_append(list, s_to_c(s));
574
	}
575
	s_free(s);
576
 
577
	if(n == 0){		/* no addressees given, just the keyword */
578
		s_free(list);
579
		return nil;
580
	}
581
	return list;
582
}
583
 
584
/*
585
 * per rfc2822 §4.5.3, permit multiple to, cc and bcc headers by
586
 * concatenating their values.
587
 */
588
 
589
static void
590
to822(Message *m, Header *h, char *p)
591
{
592
	String *s;
593
 
594
	p += strlen(h->type);
595
	s = addr822(p);
596
	if (m->to822 == nil)
597
		m->to822 = s;
598
	else if (s != nil) {
599
		s_append(m->to822, " ");
600
		s_append(m->to822, s_to_c(s));
601
		s_free(s);
602
	}
603
}
604
 
605
static void
606
cc822(Message *m, Header *h, char *p)
607
{
608
	String *s;
609
 
610
	p += strlen(h->type);
611
	s = addr822(p);
612
	if (m->cc822 == nil)
613
		m->cc822 = s;
614
	else if (s != nil) {
615
		s_append(m->cc822, " ");
616
		s_append(m->cc822, s_to_c(s));
617
		s_free(s);
618
	}
619
}
620
 
621
static void
622
bcc822(Message *m, Header *h, char *p)
623
{
624
	String *s;
625
 
626
	p += strlen(h->type);
627
	s = addr822(p);
628
	if (m->bcc822 == nil)
629
		m->bcc822 = s;
630
	else if (s != nil) {
631
		s_append(m->bcc822, " ");
632
		s_append(m->bcc822, s_to_c(s));
633
		s_free(s);
634
	}
635
}
636
 
637
static void
638
from822(Message *m, Header *h, char *p)
639
{
640
	p += strlen(h->type);
641
	s_free(m->from822);
642
	m->from822 = addr822(p);
643
}
644
 
645
static void
646
sender822(Message *m, Header *h, char *p)
647
{
648
	p += strlen(h->type);
649
	s_free(m->sender822);
650
	m->sender822 = addr822(p);
651
}
652
 
653
static void
654
replyto822(Message *m, Header *h, char *p)
655
{
656
	p += strlen(h->type);
657
	s_free(m->replyto822);
658
	m->replyto822 = addr822(p);
659
}
660
 
661
static void
662
mimeversion(Message *m, Header *h, char *p)
663
{
664
	p += strlen(h->type);
665
	s_free(m->mimeversion);
666
	m->mimeversion = addr822(p);
667
}
668
 
669
static void
670
killtrailingwhite(char *p)
671
{
672
	char *e;
673
 
674
	e = p + strlen(p) - 1;
675
	while(e > p && isspace(*e))
676
		*e-- = 0;
677
}
678
 
679
static void
680
date822(Message *m, Header *h, char *p)
681
{
682
	p += strlen(h->type);
683
	p = skipwhite(p);
684
	s_free(m->date822);
685
	m->date822 = s_copy(p);
686
	p = s_to_c(m->date822);
687
	killtrailingwhite(p);
688
}
689
 
690
static void
691
subject822(Message *m, Header *h, char *p)
692
{
693
	p += strlen(h->type);
694
	p = skipwhite(p);
695
	s_free(m->subject822);
696
	m->subject822 = s_copy(p);
697
	p = s_to_c(m->subject822);
698
	killtrailingwhite(p);
699
}
700
 
701
static void
702
inreplyto822(Message *m, Header *h, char *p)
703
{
704
	p += strlen(h->type);
705
	p = skipwhite(p);
706
	s_free(m->inreplyto822);
707
	m->inreplyto822 = s_copy(p);
708
	p = s_to_c(m->inreplyto822);
709
	killtrailingwhite(p);
710
}
711
 
712
static void
713
messageid822(Message *m, Header *h, char *p)
714
{
715
	p += strlen(h->type);
716
	p = skipwhite(p);
717
	s_free(m->messageid822);
718
	m->messageid822 = s_copy(p);
719
	p = s_to_c(m->messageid822);
720
	killtrailingwhite(p);
721
}
722
 
723
static int
724
isattribute(char **pp, char *attr)
725
{
726
	char *p;
727
	int n;
728
 
729
	n = strlen(attr);
730
	p = *pp;
731
	if(cistrncmp(p, attr, n) != 0)
732
		return 0;
733
	p += n;
734
	while(*p == ' ')
735
		p++;
736
	if(*p++ != '=')
737
		return 0;
738
	while(*p == ' ')
739
		p++;
740
	*pp = p;
741
	return 1;
742
}
743
 
744
static void
745
ctype(Message *m, Header *h, char *p)
746
{
747
	String *s;
748
 
749
	p += h->len;
750
	p = skipwhite(p);
751
 
752
	p = getstring(p, m->type, 1);
753
 
754
	while(*p){
755
		if(isattribute(&p, "boundary")){
756
			s = s_new();
757
			p = getstring(p, s, 0);
758
			m->boundary = s_reset(m->boundary);
759
			s_append(m->boundary, "--");
760
			s_append(m->boundary, s_to_c(s));
761
			s_free(s);
762
		} else if(cistrncmp(p, "multipart", 9) == 0){
763
			/*
764
			 *  the first unbounded part of a multipart message,
765
			 *  the preamble, is not displayed or saved
766
			 */
767
		} else if(isattribute(&p, "name")){
768
			if(m->filename == nil)
769
				setfilename(m, p);
770
		} else if(isattribute(&p, "charset")){
771
			p = getstring(p, s_reset(m->charset), 0);
772
		}
773
 
774
		p = skiptosemi(p);
775
	}
776
}
777
 
778
static void
779
cencoding(Message *m, Header *h, char *p)
780
{
781
	p += h->len;
782
	p = skipwhite(p);
783
	if(cistrncmp(p, "base64", 6) == 0)
784
		m->encoding = Ebase64;
785
	else if(cistrncmp(p, "quoted-printable", 16) == 0)
786
		m->encoding = Equoted;
787
}
788
 
789
static void
790
cdisposition(Message *m, Header *h, char *p)
791
{
792
	p += h->len;
793
	p = skipwhite(p);
794
	while(*p){
795
		if(cistrncmp(p, "inline", 6) == 0){
796
			m->disposition = Dinline;
797
		} else if(cistrncmp(p, "attachment", 10) == 0){
798
			m->disposition = Dfile;
799
		} else if(cistrncmp(p, "filename=", 9) == 0){
800
			p += 9;
801
			setfilename(m, p);
802
		}
803
		p = skiptosemi(p);
804
	}
805
 
806
}
807
 
808
ulong msgallocd, msgfreed;
809
 
810
Message*
811
newmessage(Message *parent)
812
{
813
	static int id;
814
	Message *m;
815
 
816
	msgallocd++;
817
 
818
	m = emalloc(sizeof(*m));
819
	memset(m, 0, sizeof(*m));
820
	m->disposition = Dnone;
821
	m->type = s_copy("text/plain");
822
	m->charset = s_copy("iso-8859-1");
823
	m->id = newid();
824
	if(parent)
825
		sprint(m->name, "%d", ++(parent->subname));
826
	if(parent == nil)
827
		parent = m;
828
	m->whole = parent;
829
	m->hlen = -1;
830
	return m;
831
}
832
 
833
// delete a message from a mailbox
834
void
835
delmessage(Mailbox *mb, Message *m)
836
{
837
	Message **l;
838
	int i;
839
 
840
	mb->vers++;
841
	msgfreed++;
842
 
843
	if(m->whole != m){
844
		// unchain from parent
845
		for(l = &m->whole->part; *l && *l != m; l = &(*l)->next)
846
			;
847
		if(*l != nil)
848
			*l = m->next;
849
 
850
		// clear out of name lookup hash table
851
		if(m->whole->whole == m->whole)
852
			hfree(PATH(mb->id, Qmbox), m->name);
853
		else
854
			hfree(PATH(m->whole->id, Qdir), m->name);
855
		for(i = 0; i < Qmax; i++)
856
			hfree(PATH(m->id, Qdir), dirtab[i]);
857
	}
858
 
859
	/* recurse through sub-parts */
860
	while(m->part)
861
		delmessage(mb, m->part);
862
 
863
	/* free memory */
864
	if(m->mallocd)
865
		free(m->start);
866
	if(m->hallocd)
867
		free(m->header);
868
	if(m->ballocd)
869
		free(m->body);
870
	s_free(m->unixfrom);
871
	s_free(m->unixdate);
872
	s_free(m->unixheader);
873
	s_free(m->from822);
874
	s_free(m->sender822);
875
	s_free(m->to822);
876
	s_free(m->bcc822);
877
	s_free(m->cc822);
878
	s_free(m->replyto822);
879
	s_free(m->date822);
880
	s_free(m->inreplyto822);
881
	s_free(m->subject822);
882
	s_free(m->messageid822);
883
	s_free(m->addrs);
884
	s_free(m->mimeversion);
885
	s_free(m->sdigest);
886
	s_free(m->boundary);
887
	s_free(m->type);
888
	s_free(m->charset);
889
	s_free(m->filename);
890
 
891
	free(m);
892
}
893
 
894
// mark messages (identified by path) for deletion
895
void
896
delmessages(int ac, char **av)
897
{
898
	Mailbox *mb;
899
	Message *m;
900
	int i, needwrite;
901
 
902
	qlock(&mbllock);
903
	for(mb = mbl; mb != nil; mb = mb->next)
904
		if(strcmp(av[0], mb->name) == 0){
905
			qlock(mb);
906
			break;
907
		}
908
	qunlock(&mbllock);
909
	if(mb == nil)
910
		return;
911
 
912
	needwrite = 0;
913
	for(i = 1; i < ac; i++){
914
		for(m = mb->root->part; m != nil; m = m->next)
915
			if(strcmp(m->name, av[i]) == 0){
916
				if(!m->deleted){
917
					mailplumb(mb, m, 1);
918
					needwrite = 1;
919
					m->deleted = 1;
920
					logmsg("deleting", m);
921
				}
922
				break;
923
			}
924
	}
925
	if(needwrite)
926
		syncmbox(mb, 1);
927
	qunlock(mb);
928
}
929
 
930
/*
931
 *  the following are called with the mailbox qlocked
932
 */
933
void
934
msgincref(Message *m)
935
{
936
	m->refs++;
937
}
938
void
939
msgdecref(Mailbox *mb, Message *m)
940
{
941
	m->refs--;
942
	if(m->refs == 0 && m->deleted)
943
		syncmbox(mb, 1);
944
}
945
 
946
/*
947
 *  the following are called with mbllock'd
948
 */
949
void
950
mboxincref(Mailbox *mb)
951
{
952
	assert(mb->refs > 0);
953
	mb->refs++;
954
}
955
void
956
mboxdecref(Mailbox *mb)
957
{
958
	assert(mb->refs > 0);
959
	qlock(mb);
960
	mb->refs--;
961
	if(mb->refs == 0){
962
		delmessage(mb, mb->root);
963
		if(mb->ctl)
964
			hfree(PATH(mb->id, Qmbox), "ctl");
965
		if(mb->close)
966
			(*mb->close)(mb);
967
		free(mb);
968
	} else
969
		qunlock(mb);
970
}
971
 
972
int
973
cistrncmp(char *a, char *b, int n)
974
{
975
	while(n-- > 0){
976
		if(tolower(*a++) != tolower(*b++))
977
			return -1;
978
	}
979
	return 0;
980
}
981
 
982
int
983
cistrcmp(char *a, char *b)
984
{
985
	for(;;){
986
		if(tolower(*a) != tolower(*b++))
987
			return -1;
988
		if(*a++ == 0)
989
			break;
990
	}
991
	return 0;
992
}
993
 
994
static char*
995
skipwhite(char *p)
996
{
997
	while(isspace(*p))
998
		p++;
999
	return p;
1000
}
1001
 
1002
static char*
1003
skiptosemi(char *p)
1004
{
1005
	while(*p && *p != ';')
1006
		p++;
1007
	while(*p == ';' || isspace(*p))
1008
		p++;
1009
	return p;
1010
}
1011
 
1012
static char*
1013
getstring(char *p, String *s, int dolower)
1014
{
1015
	s = s_reset(s);
1016
	p = skipwhite(p);
1017
	if(*p == '"'){
1018
		p++;
1019
		for(;*p && *p != '"'; p++)
1020
			if(dolower)
1021
				s_putc(s, tolower(*p));
1022
			else
1023
				s_putc(s, *p);
1024
		if(*p == '"')
1025
			p++;
1026
		s_terminate(s);
1027
 
1028
		return p;
1029
	}
1030
 
1031
	for(; *p && !isspace(*p) && *p != ';'; p++)
1032
		if(dolower)
1033
			s_putc(s, tolower(*p));
1034
		else
1035
			s_putc(s, *p);
1036
	s_terminate(s);
1037
 
1038
	return p;
1039
}
1040
 
1041
static void
1042
setfilename(Message *m, char *p)
1043
{
1044
	m->filename = s_reset(m->filename);
1045
	getstring(p, m->filename, 0);
1046
	for(p = s_to_c(m->filename); *p; p++)
1047
		if(*p == ' ' || *p == '\t' || *p == ';')
1048
			*p = '_';
1049
}
1050
 
1051
//
1052
// undecode message body
1053
//
1054
void
1055
decode(Message *m)
1056
{
1057
	int i, len;
1058
	char *x;
1059
 
1060
	if(m->decoded)
1061
		return;
1062
	switch(m->encoding){
1063
	case Ebase64:
1064
		len = m->bend - m->body;
1065
		i = (len*3)/4+1;	// room for max chars + null
1066
		x = emalloc(i);
1067
		len = dec64((uchar*)x, i, m->body, len);
1068
		if(m->ballocd)
1069
			free(m->body);
1070
		m->body = x;
1071
		m->bend = x + len;
1072
		m->ballocd = 1;
1073
		break;
1074
	case Equoted:
1075
		len = m->bend - m->body;
1076
		x = emalloc(len+2);	// room for null and possible extra nl
1077
		len = decquoted(x, m->body, m->bend, 0);
1078
		if(m->ballocd)
1079
			free(m->body);
1080
		m->body = x;
1081
		m->bend = x + len;
1082
		m->ballocd = 1;
1083
		break;
1084
	default:
1085
		break;
1086
	}
1087
	m->decoded = 1;
1088
}
1089
 
1090
// convert latin1 to utf
1091
void
1092
convert(Message *m)
1093
{
1094
	int len;
1095
	char *x;
1096
 
1097
	// don't convert if we're not a leaf, not text, or already converted
1098
	if(m->converted)
1099
		return;
1100
	if(m->part != nil)
1101
		return;
1102
	if(cistrncmp(s_to_c(m->type), "text", 4) != 0)
1103
		return;
1104
 
1105
	len = xtoutf(s_to_c(m->charset), &x, m->body, m->bend);
1106
	if(len > 0){
1107
		if(m->ballocd)
1108
			free(m->body);
1109
		m->body = x;
1110
		m->bend = x + len;
1111
		m->ballocd = 1;
1112
	}
1113
	m->converted = 1;
1114
}
1115
 
1116
static int
1117
hex2int(int x)
1118
{
1119
	if(x >= '0' && x <= '9')
1120
		return x - '0';
1121
	if(x >= 'A' && x <= 'F')
1122
		return (x - 'A') + 10;
1123
	if(x >= 'a' && x <= 'f')
1124
		return (x - 'a') + 10;
1125
	return -1;
1126
}
1127
 
1128
// underscores are translated in 2047 headers (uscores=1) 
1129
// but not in the body (uscores=0)
1130
static char*
1131
decquotedline(char *out, char *in, char *e, int uscores)
1132
{
1133
	int c, c2, soft;
1134
 
1135
	/* dump trailing white space */
1136
	while(e >= in && (*e == ' ' || *e == '\t' || *e == '\r' || *e == '\n'))
1137
		e--;
1138
 
1139
	/* trailing '=' means no newline */
1140
	if(*e == '='){
1141
		soft = 1;
1142
		e--;
1143
	} else
1144
		soft = 0;
1145
 
1146
	while(in <= e){
1147
		c = (*in++) & 0xff;
1148
		switch(c){
1149
		case '_':
1150
			if(uscores){
1151
				*out++ = ' ';
1152
				break;
1153
			}
1154
		default:
1155
			*out++ = c;
1156
			break;
1157
		case '=':
1158
			c  = hex2int(*in++);
1159
			c2 = hex2int(*in++);
1160
			if (c != -1 && c2 != -1)
1161
				*out++ = c<<4 | c2;
1162
			else {
1163
				*out++ = '=';
1164
				in -= 2;
1165
			}
1166
			break;
1167
		}
1168
	}
1169
	if(!soft)
1170
		*out++ = '\n';
1171
	*out = 0;
1172
 
1173
	return out;
1174
}
1175
 
1176
int
1177
decquoted(char *out, char *in, char *e, int uscores)
1178
{
1179
	char *p, *nl;
1180
 
1181
	p = out;
1182
	while((nl = strchr(in, '\n')) != nil && nl < e){
1183
		p = decquotedline(p, in, nl, uscores);
1184
		in = nl + 1;
1185
	}
1186
	if(in < e)
1187
		p = decquotedline(p, in, e-1, uscores);
1188
 
1189
	// make sure we end with a new line
1190
	if(*(p-1) != '\n'){
1191
		*p++ = '\n';
1192
		*p = 0;
1193
	}
1194
 
1195
	return p - out;
1196
}
1197
 
1198
static char*
1199
lowercase(char *p)
1200
{
1201
	char *op;
1202
	int c;
1203
 
1204
	for(op = p; c = *p; p++)
1205
		if(isupper(c))
1206
			*p = tolower(c);
1207
	return op;
1208
}
1209
 
1210
// translate latin1 directly since it fits neatly in utf
1211
static int
1212
latin1toutf(char **out, char *in, char *e)
1213
{
1214
	int n;
1215
	char *p;
1216
	Rune r;
1217
 
1218
	n = 0;
1219
	for(p = in; p < e; p++)
1220
		if(*p & 0x80)
1221
			n++;
1222
	if(n == 0)
1223
		return 0;
1224
 
1225
	n += e-in;
1226
	*out = p = malloc(n+1);
1227
	if(p == nil)
1228
		return 0;
1229
 
1230
	for(; in < e; in++){
1231
		r = (uchar)*in;
1232
		p += runetochar(p, &r);
1233
	}
1234
	*p = 0;
1235
	return p - *out;
1236
}
1237
 
1238
// translate any thing using the tcs program
1239
int
1240
xtoutf(char *charset, char **out, char *in, char *e)
1241
{
1242
	char *av[4];
1243
	int totcs[2];
1244
	int fromtcs[2];
1245
	int n, len, sofar;
1246
	char *p;
1247
 
1248
	// might not need to convert
1249
	if(cistrcmp(charset, "us-ascii") == 0 || cistrcmp(charset, "utf-8") == 0)
1250
		return 0;
1251
	if(cistrcmp(charset, "iso-8859-1") == 0)
1252
		return latin1toutf(out, in, e);
1253
 
1254
	len = e-in+1;
1255
	sofar = 0;
1256
	*out = p = malloc(len+1);
1257
	if(p == nil)
1258
		return 0;
1259
 
1260
	av[0] = charset;
1261
	av[1] = "-f";
1262
	av[2] = charset;
1263
	av[3] = 0;
1264
	if(pipe(totcs) < 0)
1265
		goto error;
1266
	if(pipe(fromtcs) < 0){
1267
		close(totcs[0]); close(totcs[1]);
1268
		goto error;
1269
	}
1270
	switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
1271
	case -1:
1272
		close(fromtcs[0]); close(fromtcs[1]);
1273
		close(totcs[0]); close(totcs[1]);
1274
		goto error;
1275
	case 0:
1276
		close(fromtcs[0]); close(totcs[1]);
1277
		dup(fromtcs[1], 1);
1278
		dup(totcs[0], 0);
1279
		close(fromtcs[1]); close(totcs[0]);
1280
		dup(open("/dev/null", OWRITE), 2);
1281
		exec("/bin/tcs", av);
1282
		_exits(0);
1283
	default:
1284
		close(fromtcs[1]); close(totcs[0]);
1285
		switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
1286
		case -1:
1287
			close(fromtcs[0]); close(totcs[1]);
1288
			goto error;
1289
		case 0:
1290
			close(fromtcs[0]);
1291
			while(in < e){
1292
				n = write(totcs[1], in, e-in);
1293
				if(n <= 0)
1294
					break;
1295
				in += n;
1296
			}
1297
			close(totcs[1]);
1298
			_exits(0);
1299
		default:
1300
			close(totcs[1]);
1301
			for(;;){
1302
				n = read(fromtcs[0], &p[sofar], len-sofar);
1303
				if(n <= 0)
1304
					break;
1305
				sofar += n;
1306
				p[sofar] = 0;
1307
				if(sofar == len){
1308
					len += 1024;
1309
					p = realloc(p, len+1);
1310
					if(p == nil)
1311
						goto error;
1312
					*out = p;
1313
				}
1314
			}
1315
			close(fromtcs[0]);
1316
			break;
1317
		}
1318
		break;
1319
	}
1320
	if(sofar == 0)
1321
		goto error;
1322
	return sofar;
1323
 
1324
error:
1325
	free(*out);
1326
	*out = nil;
1327
	return 0;
1328
}
1329
 
1330
void *
1331
emalloc(ulong n)
1332
{
1333
	void *p;
1334
 
1335
	p = mallocz(n, 1);
1336
	if(!p){
1337
		fprint(2, "%s: out of memory alloc %lud\n", argv0, n);
1338
		exits("out of memory");
1339
	}
1340
	setmalloctag(p, getcallerpc(&n));
1341
	return p;
1342
}
1343
 
1344
void *
1345
erealloc(void *p, ulong n)
1346
{
1347
	if(n == 0)
1348
		n = 1;
1349
	p = realloc(p, n);
1350
	if(!p){
1351
		fprint(2, "%s: out of memory realloc %lud\n", argv0, n);
1352
		exits("out of memory");
1353
	}
1354
	setrealloctag(p, getcallerpc(&p));
1355
	return p;
1356
}
1357
 
1358
void
1359
mailplumb(Mailbox *mb, Message *m, int delete)
1360
{
1361
	Plumbmsg p;
1362
	Plumbattr a[7];
1363
	char buf[256];
1364
	int ai;
1365
	char lenstr[10], *from, *subject, *date;
1366
	static int fd = -1;
1367
 
1368
	if(m->subject822 == nil)
1369
		subject = "";
1370
	else
1371
		subject = s_to_c(m->subject822);
1372
 
1373
	if(m->from822 != nil)
1374
		from = s_to_c(m->from822);
1375
	else if(m->unixfrom != nil)
1376
		from = s_to_c(m->unixfrom);
1377
	else
1378
		from = "";
1379
 
1380
	if(m->unixdate != nil)
1381
		date = s_to_c(m->unixdate);
1382
	else
1383
		date = "";
1384
 
1385
	sprint(lenstr, "%ld", m->end-m->start);
1386
 
1387
	if(biffing && !delete)
1388
		print("[ %s / %s / %s ]\n", from, subject, lenstr);
1389
 
1390
	if(!plumbing)
1391
		return;
1392
 
1393
	if(fd < 0)
1394
		fd = plumbopen("send", OWRITE);
1395
	if(fd < 0)
1396
		return;
1397
 
1398
	p.src = "mailfs";
1399
	p.dst = "seemail";
1400
	p.wdir = "/mail/fs";
1401
	p.type = "text";
1402
 
1403
	ai = 0;
1404
	a[ai].name = "filetype";
1405
	a[ai].value = "mail";
1406
 
1407
	a[++ai].name = "sender";
1408
	a[ai].value = from;
1409
	a[ai-1].next = &a[ai];
1410
 
1411
	a[++ai].name = "length";
1412
	a[ai].value = lenstr;
1413
	a[ai-1].next = &a[ai];
1414
 
1415
	a[++ai].name = "mailtype";
1416
	a[ai].value = delete?"delete":"new";
1417
	a[ai-1].next = &a[ai];
1418
 
1419
	a[++ai].name = "date";
1420
	a[ai].value = date;
1421
	a[ai-1].next = &a[ai];
1422
 
1423
	if(m->sdigest){
1424
		a[++ai].name = "digest";
1425
		a[ai].value = s_to_c(m->sdigest);
1426
		a[ai-1].next = &a[ai];
1427
	}
1428
 
1429
	a[ai].next = nil;
1430
 
1431
	p.attr = a;
1432
	snprint(buf, sizeof(buf), "%s/%s/%s",
1433
		mntpt, mb->name, m->name);
1434
	p.ndata = strlen(buf);
1435
	p.data = buf;
1436
 
1437
	plumbsend(fd, &p);
1438
}
1439
 
1440
//
1441
// count the number of lines in the body (for imap4)
1442
//
1443
void
1444
countlines(Message *m)
1445
{
1446
	int i;
1447
	char *p;
1448
 
1449
	i = 0;
1450
	for(p = strchr(m->rbody, '\n'); p != nil && p < m->rbend; p = strchr(p+1, '\n'))
1451
		i++;
1452
	sprint(m->lines, "%d", i);
1453
}
1454
 
1455
char *LOG = "fs";
1456
 
1457
void
1458
logmsg(char *s, Message *m)
1459
{
1460
	int pid;
1461
 
1462
	if(!logging)
1463
		return;
1464
	pid = getpid();
1465
	if(m == nil)
1466
		syslog(0, LOG, "%s.%d: %s", user, pid, s);
1467
	else
1468
		syslog(0, LOG, "%s.%d: %s msg from %s digest %s",
1469
			user, pid, s,
1470
			m->from822 ? s_to_c(m->from822) : "?",
1471
			s_to_c(m->sdigest));
1472
}
1473
 
1474
/*
1475
 *  squeeze nulls out of the body
1476
 */
1477
static void
1478
nullsqueeze(Message *m)
1479
{
1480
	char *p, *q;
1481
 
1482
	q = memchr(m->body, 0, m->end-m->body);
1483
	if(q == nil)
1484
		return;
1485
 
1486
	for(p = m->body; q < m->end; q++){
1487
		if(*q == 0)
1488
			continue;
1489
		*p++ = *q;
1490
	}
1491
	m->bend = m->rbend = m->end = p;
1492
}
1493
 
1494
 
1495
//
1496
// convert an RFC822 date into a Unix style date
1497
// for when the Unix From line isn't there (e.g. POP3).
1498
// enough client programs depend on having a Unix date
1499
// that it's easiest to write this conversion code once, right here.
1500
//
1501
// people don't follow RFC822 particularly closely,
1502
// so we use strtotm, which is a bunch of heuristics.
1503
//
1504
 
1505
extern int strtotm(char*, Tm*);
1506
String*
1507
date822tounix(char *s)
1508
{
1509
	char *p, *q;
1510
	Tm tm;
1511
 
1512
	if(strtotm(s, &tm) < 0)
1513
		return nil;
1514
 
1515
	p = asctime(&tm);
1516
	if(q = strchr(p, '\n'))
1517
		*q = '\0';
1518
	return s_copy(p);
1519
}
1520