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 <auth.h>
3
#include <fcall.h>
4
#include <libsec.h>
5
#include <ctype.h>
6
#include "dat.h"
7
 
8
enum
9
{
10
	OPERM	= 0x3,		// mask of all permission types in open mode
11
};
12
 
13
typedef struct Fid Fid;
14
 
15
struct Fid
16
{
17
	Qid	qid;
18
	short	busy;
19
	short	open;
20
	int	fid;
21
	Fid	*next;
22
	Mailbox	*mb;
23
	Message	*m;
24
	Message *mtop;		// top level message
25
 
26
	//finger pointers to speed up reads of large directories
27
	long	foff;	// offset/DIRLEN of finger
28
	Message	*fptr;	// pointer to message at off
29
	int	fvers;	// mailbox version when finger was saved
30
};
31
 
32
ulong	path;		// incremented for each new file
33
Fid	*fids;
34
int	mfd[2];
35
char	user[Elemlen];
36
int	messagesize = 4*1024+IOHDRSZ;
37
uchar	mdata[8*1024+IOHDRSZ];
38
uchar	mbuf[8*1024+IOHDRSZ];
39
Fcall	thdr;
40
Fcall	rhdr;
41
int	fflg;
42
char	*mntpt;
43
int	biffing;
44
int	plumbing = 1;
45
 
46
QLock	mbllock;
47
Mailbox	*mbl;
48
 
49
Fid		*newfid(int);
50
void		error(char*);
51
void		io(void);
52
void		*erealloc(void*, ulong);
53
void		*emalloc(ulong);
54
void		usage(void);
55
void		reader(void);
56
int		readheader(Message*, char*, int, int);
57
int		cistrncmp(char*, char*, int);
58
int		tokenconvert(String*, char*, int);
59
String*		stringconvert(String*, char*, int);
60
void		post(char*, char*, int);
61
 
62
char	*rflush(Fid*), *rauth(Fid*),
63
	*rattach(Fid*), *rwalk(Fid*),
64
	*ropen(Fid*), *rcreate(Fid*),
65
	*rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
66
	*rremove(Fid*), *rstat(Fid*), *rwstat(Fid*),
67
	*rversion(Fid*);
68
 
69
char 	*(*fcalls[])(Fid*) = {
70
	[Tflush]	rflush,
71
	[Tversion]	rversion,
72
	[Tauth]	rauth,
73
	[Tattach]	rattach,
74
	[Twalk]		rwalk,
75
	[Topen]		ropen,
76
	[Tcreate]	rcreate,
77
	[Tread]		rread,
78
	[Twrite]	rwrite,
79
	[Tclunk]	rclunk,
80
	[Tremove]	rremove,
81
	[Tstat]		rstat,
82
	[Twstat]	rwstat,
83
};
84
 
85
char	Eperm[] =	"permission denied";
86
char	Enotdir[] =	"not a directory";
87
char	Enoauth[] =	"upas/fs: authentication not required";
88
char	Enotexist[] =	"file does not exist";
89
char	Einuse[] =	"file in use";
90
char	Eexist[] =	"file exists";
91
char	Enotowner[] =	"not owner";
92
char	Eisopen[] = 	"file already open for I/O";
93
char	Excl[] = 	"exclusive use file already open";
94
char	Ename[] = 	"illegal name";
95
char	Ebadctl[] =	"unknown control message";
96
 
97
char *dirtab[] =
98
{
99
[Qdir]		".",
100
[Qbody]		"body",
101
[Qbcc]		"bcc",
102
[Qcc]		"cc",
103
[Qdate]		"date",
104
[Qdigest]	"digest",
105
[Qdisposition]	"disposition",
106
[Qfilename]	"filename",
107
[Qfrom]		"from",
108
[Qheader]	"header",
109
[Qinfo]		"info",
110
[Qinreplyto]	"inreplyto",
111
[Qlines]	"lines",
112
[Qmimeheader]	"mimeheader",
113
[Qmessageid]	"messageid",
114
[Qraw]		"raw",
115
[Qrawunix]	"rawunix",
116
[Qrawbody]	"rawbody",
117
[Qrawheader]	"rawheader",
118
[Qreplyto]	"replyto",
119
[Qsender]	"sender",
120
[Qsubject]	"subject",
121
[Qto]		"to",
122
[Qtype]		"type",
123
[Qunixdate]	"unixdate",
124
[Qunixheader]	"unixheader",
125
[Qctl]		"ctl",
126
[Qmboxctl]	"ctl",
127
};
128
 
129
enum
130
{
131
	Hsize=	1277,
132
};
133
 
134
Hash	*htab[Hsize];
135
 
136
int	debug;
137
int	fflag;
138
int	logging;
139
 
140
void
141
usage(void)
142
{
143
	fprint(2, "usage: upas/fs [-bdlnps] [-f mboxfile] [-m mountpoint]\n");
144
	exits("usage");
145
}
146
 
147
void
148
notifyf(void *a, char *s)
149
{
150
	USED(a);
151
	if(strncmp(s, "interrupt", 9) == 0)
152
		noted(NCONT);
153
	noted(NDFLT);
154
}
155
 
156
void
157
main(int argc, char *argv[])
158
{
159
	int p[2], std, nodflt;
160
	char maildir[128];
161
	char mbox[128];
162
	char *mboxfile, *err;
163
	char srvfile[64];
164
	int srvpost;
165
 
166
	rfork(RFNOTEG);
167
	mntpt = nil;
168
	fflag = 0;
169
	mboxfile = nil;
170
	std = 0;
171
	nodflt = 0;
172
	srvpost = 0;
173
 
174
	ARGBEGIN{
175
	case 'b':
176
		biffing = 1;
177
		break;
178
	case 'f':
179
		fflag = 1;
180
		mboxfile = EARGF(usage());
181
		break;
182
	case 'm':
183
		mntpt = EARGF(usage());
184
		break;
185
	case 'd':
186
		debug = 1;
187
		break;
188
	case 'p':
189
		plumbing = 0;
190
		break;
191
	case 's':
192
		srvpost = 1;
193
		break;
194
	case 'l':
195
		logging = 1;
196
		break;
197
	case 'n':
198
		nodflt = 1;
199
		break;
200
	default:
201
		usage();
202
	}ARGEND
203
 
204
	if(argc)
205
		usage();
206
	if(pipe(p) < 0)
207
		error("pipe failed");
208
	mfd[0] = p[0];
209
	mfd[1] = p[0];
210
 
211
	notify(notifyf);
212
	strcpy(user, getuser());
213
	if(mntpt == nil){
214
		snprint(maildir, sizeof(maildir), "/mail/fs");
215
		mntpt = maildir;
216
	}
217
	if(mboxfile == nil && !nodflt){
218
		snprint(mbox, sizeof(mbox), "/mail/box/%s/mbox", user);
219
		mboxfile = mbox;
220
		std = 1;
221
	}
222
 
223
	if(debug)
224
		fmtinstall('F', fcallfmt);
225
 
226
	if(mboxfile != nil){
227
		err = newmbox(mboxfile, "mbox", std);
228
		if(err != nil)
229
			sysfatal("opening %s: %s", mboxfile, err);
230
	}
231
 
232
	switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG|RFREND)){
233
	case -1:
234
		error("fork");
235
	case 0:
236
		henter(PATH(0, Qtop), dirtab[Qctl],
237
			(Qid){PATH(0, Qctl), 0, QTFILE}, nil, nil);
238
		close(p[1]);
239
		io();
240
		postnote(PNGROUP, getpid(), "die yankee pig dog");
241
		break;
242
	default:
243
		close(p[0]);	/* don't deadlock if child fails */
244
		if(srvpost){
245
			sprint(srvfile, "/srv/upasfs.%s", user);
246
			post(srvfile, "upasfs", p[1]);
247
		} else {
248
			if(mount(p[1], -1, mntpt, MREPL, "") < 0)
249
				error("mount failed");
250
		}
251
	}
252
	exits(0);
253
}
254
 
255
static int
256
fileinfo(Message *m, int t, char **pp)
257
{
258
	char *p;
259
	int len;
260
 
261
	p = "";
262
	len = 0;
263
	switch(t){
264
	case Qbody:
265
		p = m->body;
266
		len = m->bend - m->body;
267
		break;
268
	case Qbcc:
269
		if(m->bcc822){
270
			p = s_to_c(m->bcc822);
271
			len = strlen(p);
272
		}
273
		break;
274
	case Qcc:
275
		if(m->cc822){
276
			p = s_to_c(m->cc822);
277
			len = strlen(p);
278
		}
279
		break;
280
	case Qdisposition:
281
		switch(m->disposition){
282
		case Dinline:
283
			p = "inline";
284
			break;
285
		case Dfile:
286
			p = "file";
287
			break;
288
		}
289
		len = strlen(p);
290
		break;
291
	case Qdate:
292
		if(m->date822){
293
			p = s_to_c(m->date822);
294
			len = strlen(p);
295
		} else if(m->unixdate != nil){
296
			p = s_to_c(m->unixdate);
297
			len = strlen(p);
298
		}
299
		break;
300
	case Qfilename:
301
		if(m->filename){
302
			p = s_to_c(m->filename);
303
			len = strlen(p);
304
		}
305
		break;
306
	case Qinreplyto:
307
		if(m->inreplyto822){
308
			p = s_to_c(m->inreplyto822);
309
			len = strlen(p);
310
		}
311
		break;
312
	case Qmessageid:
313
		if(m->messageid822){
314
			p = s_to_c(m->messageid822);
315
			len = strlen(p);
316
		}
317
		break;
318
	case Qfrom:
319
		if(m->from822){
320
			p = s_to_c(m->from822);
321
			len = strlen(p);
322
		} else if(m->unixfrom != nil){
323
			p = s_to_c(m->unixfrom);
324
			len = strlen(p);
325
		}
326
		break;
327
	case Qheader:
328
		p = m->header;
329
		len = headerlen(m);
330
		break;
331
	case Qlines:
332
		p = m->lines;
333
		if(*p == 0)
334
			countlines(m);
335
		len = strlen(m->lines);
336
		break;
337
	case Qraw:
338
		p = m->start;
339
		if(strncmp(m->start, "From ", 5) == 0){
340
			p = strchr(p, '\n');
341
			if(p == nil)
342
				p = m->start;
343
			else
344
				p++;
345
		}
346
		len = m->end - p;
347
		break;
348
	case Qrawunix:
349
		p = m->start;
350
		len = m->end - p;
351
		break;
352
	case Qrawbody:
353
		p = m->rbody;
354
		len = m->rbend - p;
355
		break;
356
	case Qrawheader:
357
		p = m->header;
358
		len = m->hend - p;
359
		break;
360
	case Qmimeheader:
361
		p = m->mheader;
362
		len = m->mhend - p;
363
		break;
364
	case Qreplyto:
365
		p = nil;
366
		if(m->replyto822 != nil){
367
			p = s_to_c(m->replyto822);
368
			len = strlen(p);
369
		} else if(m->from822 != nil){
370
			p = s_to_c(m->from822);
371
			len = strlen(p);
372
		} else if(m->sender822 != nil){
373
			p = s_to_c(m->sender822);
374
			len = strlen(p);
375
		} else if(m->unixfrom != nil){
376
			p = s_to_c(m->unixfrom);
377
			len = strlen(p);
378
		}
379
		break;
380
	case Qsender:
381
		if(m->sender822){
382
			p = s_to_c(m->sender822);
383
			len = strlen(p);
384
		}
385
		break;
386
	case Qsubject:
387
		p = nil;
388
		if(m->subject822){
389
			p = s_to_c(m->subject822);
390
			len = strlen(p);
391
		}
392
		break;
393
	case Qto:
394
		if(m->to822){
395
			p = s_to_c(m->to822);
396
			len = strlen(p);
397
		}
398
		break;
399
	case Qtype:
400
		if(m->type){
401
			p = s_to_c(m->type);
402
			len = strlen(p);
403
		}
404
		break;
405
	case Qunixdate:
406
		if(m->unixdate){
407
			p = s_to_c(m->unixdate);
408
			len = strlen(p);
409
		}
410
		break;
411
	case Qunixheader:
412
		if(m->unixheader){
413
			p = s_to_c(m->unixheader);
414
			len = s_len(m->unixheader);
415
		}
416
		break;
417
	case Qdigest:
418
		if(m->sdigest){
419
			p = s_to_c(m->sdigest);
420
			len = strlen(p);
421
		}
422
		break;
423
	}
424
	*pp = p;
425
	return len;
426
}
427
 
428
int infofields[] = {
429
	Qfrom,
430
	Qto,
431
	Qcc,
432
	Qreplyto,
433
	Qunixdate,
434
	Qsubject,
435
	Qtype,
436
	Qdisposition,
437
	Qfilename,
438
	Qdigest,
439
	Qbcc,
440
	Qinreplyto,
441
	Qdate,
442
	Qsender,
443
	Qmessageid,
444
	Qlines,
445
	-1,
446
};
447
 
448
static int
449
readinfo(Message *m, char *buf, long off, int count)
450
{
451
	char *p;
452
	int len, i, n;
453
	String *s;
454
 
455
	s = s_new();
456
	len = 0;
457
	for(i = 0; len < count && infofields[i] >= 0; i++){
458
		n = fileinfo(m, infofields[i], &p);
459
		s = stringconvert(s, p, n);
460
		s_append(s, "\n");
461
		p = s_to_c(s);
462
		n = strlen(p);
463
		if(off > 0){
464
			if(off >= n){
465
				off -= n;
466
				continue;
467
			}
468
			p += off;
469
			n -= off;
470
			off = 0;
471
		}
472
		if(n > count - len)
473
			n = count - len;
474
		if(buf)
475
			memmove(buf+len, p, n);
476
		len += n;
477
	}
478
	s_free(s);
479
	return len;
480
}
481
 
482
static void
483
mkstat(Dir *d, Mailbox *mb, Message *m, int t)
484
{
485
	char *p;
486
 
487
	d->uid = user;
488
	d->gid = user;
489
	d->muid = user;
490
	d->mode = 0444;
491
	d->qid.vers = 0;
492
	d->qid.type = QTFILE;
493
	d->type = 0;
494
	d->dev = 0;
495
	if(mb != nil && mb->d != nil){
496
		d->atime = mb->d->atime;
497
		d->mtime = mb->d->mtime;
498
	} else {
499
		d->atime = time(0);
500
		d->mtime = d->atime;
501
	}
502
 
503
	switch(t){
504
	case Qtop:
505
		d->name = ".";
506
		d->mode = DMDIR|0555;
507
		d->atime = d->mtime = time(0);
508
		d->length = 0;
509
		d->qid.path = PATH(0, Qtop);
510
		d->qid.type = QTDIR;
511
		break;
512
	case Qmbox:
513
		d->name = mb->name;
514
		d->mode = DMDIR|0555;
515
		d->length = 0;
516
		d->qid.path = PATH(mb->id, Qmbox);
517
		d->qid.type = QTDIR;
518
		d->qid.vers = mb->vers;
519
		break;
520
	case Qdir:
521
		d->name = m->name;
522
		d->mode = DMDIR|0555;
523
		d->length = 0;
524
		d->qid.path = PATH(m->id, Qdir);
525
		d->qid.type = QTDIR;
526
		break;
527
	case Qctl:
528
		d->name = dirtab[t];
529
		d->mode = 0666;
530
		d->atime = d->mtime = time(0);
531
		d->length = 0;
532
		d->qid.path = PATH(0, Qctl);
533
		break;
534
	case Qmboxctl:
535
		d->name = dirtab[t];
536
		d->mode = 0222;
537
		d->atime = d->mtime = time(0);
538
		d->length = 0;
539
		d->qid.path = PATH(mb->id, Qmboxctl);
540
		break;
541
	case Qinfo:
542
		d->name = dirtab[t];
543
		d->length = readinfo(m, nil, 0, 1<<30);
544
		d->qid.path = PATH(m->id, t);
545
		break;
546
	default:
547
		d->name = dirtab[t];
548
		d->length = fileinfo(m, t, &p);
549
		d->qid.path = PATH(m->id, t);
550
		break;
551
	}
552
}
553
 
554
char*
555
rversion(Fid*)
556
{
557
	Fid *f;
558
 
559
	if(thdr.msize < 256)
560
		return "max messagesize too small";
561
	if(thdr.msize < messagesize)
562
		messagesize = thdr.msize;
563
	rhdr.msize = messagesize;
564
	if(strncmp(thdr.version, "9P2000", 6) != 0)
565
		return "unknown 9P version";
566
	else
567
		rhdr.version = "9P2000";
568
	for(f = fids; f; f = f->next)
569
		if(f->busy)
570
			rclunk(f);
571
	return nil;
572
}
573
 
574
char*
575
rauth(Fid*)
576
{
577
	return Enoauth;
578
}
579
 
580
char*
581
rflush(Fid *f)
582
{
583
	USED(f);
584
	return 0;
585
}
586
 
587
char*
588
rattach(Fid *f)
589
{
590
	f->busy = 1;
591
	f->m = nil;
592
	f->mb = nil;
593
	f->qid.path = PATH(0, Qtop);
594
	f->qid.type = QTDIR;
595
	f->qid.vers = 0;
596
	rhdr.qid = f->qid;
597
	if(strcmp(thdr.uname, user) != 0)
598
		return Eperm;
599
	return 0;
600
}
601
 
602
static Fid*
603
doclone(Fid *f, int nfid)
604
{
605
	Fid *nf;
606
 
607
	nf = newfid(nfid);
608
	if(nf->busy)
609
		return nil;
610
	nf->busy = 1;
611
	nf->open = 0;
612
	nf->m = f->m;
613
	nf->mtop = f->mtop;
614
	nf->mb = f->mb;
615
	if(f->mb != nil)
616
		mboxincref(f->mb);
617
	if(f->mtop != nil){
618
		qlock(f->mb);
619
		msgincref(f->mtop);
620
		qunlock(f->mb);
621
	}
622
	nf->qid = f->qid;
623
	return nf;
624
}
625
 
626
char*
627
dowalk(Fid *f, char *name)
628
{
629
	int t;
630
	Mailbox *omb, *mb;
631
	char *rv, *p;
632
	Hash *h;
633
 
634
	t = FILE(f->qid.path);
635
 
636
	rv = Enotexist;
637
 
638
	omb = f->mb;
639
	if(omb)
640
		qlock(omb);
641
	else
642
		qlock(&mbllock);
643
 
644
	// this must catch everything except . and ..
645
retry:
646
	h = hlook(f->qid.path, name);
647
	if(h != nil){
648
		f->mb = h->mb;
649
		f->m = h->m;
650
		switch(t){
651
		case Qtop:
652
			if(f->mb != nil)
653
				mboxincref(f->mb);
654
			break;
655
		case Qmbox:
656
			if(f->m){
657
				msgincref(f->m);
658
				f->mtop = f->m;
659
			}
660
			break;
661
		}
662
		f->qid = h->qid;
663
		rv = nil;
664
	} else if((p = strchr(name, '.')) != nil && *name != '.'){
665
		*p = 0;
666
		goto retry;
667
	}
668
 
669
	if(omb)
670
		qunlock(omb);
671
	else
672
		qunlock(&mbllock);
673
	if(rv == nil)
674
		return rv;
675
 
676
	if(strcmp(name, ".") == 0)
677
		return nil;
678
 
679
	if(f->qid.type != QTDIR)
680
		return Enotdir;
681
 
682
	if(strcmp(name, "..") == 0){
683
		switch(t){
684
		case Qtop:
685
			f->qid.path = PATH(0, Qtop);
686
			f->qid.type = QTDIR;
687
			f->qid.vers = 0;
688
			break;
689
		case Qmbox:
690
			f->qid.path = PATH(0, Qtop);
691
			f->qid.type = QTDIR;
692
			f->qid.vers = 0;
693
			qlock(&mbllock);
694
			mb = f->mb;
695
			f->mb = nil;
696
			mboxdecref(mb);
697
			qunlock(&mbllock);
698
			break;
699
		case Qdir:
700
			qlock(f->mb);
701
			if(f->m->whole == f->mb->root){
702
				f->qid.path = PATH(f->mb->id, Qmbox);
703
				f->qid.type = QTDIR;
704
				f->qid.vers = f->mb->d->qid.vers;
705
				msgdecref(f->mb, f->mtop);
706
				f->m = f->mtop = nil;
707
			} else {
708
				f->m = f->m->whole;
709
				f->qid.path = PATH(f->m->id, Qdir);
710
				f->qid.type = QTDIR;
711
			}
712
			qunlock(f->mb);
713
			break;
714
		}
715
		rv = nil;
716
	}
717
	return rv;
718
}
719
 
720
char*
721
rwalk(Fid *f)
722
{
723
	Fid *nf;
724
	char *rv;
725
	int i;
726
 
727
	if(f->open)
728
		return Eisopen;
729
 
730
	rhdr.nwqid = 0;
731
	nf = nil;
732
 
733
	/* clone if requested */
734
	if(thdr.newfid != thdr.fid){
735
		nf = doclone(f, thdr.newfid);
736
		if(nf == nil)
737
			return "new fid in use";
738
		f = nf;
739
	}
740
 
741
	/* if it's just a clone, return */
742
	if(thdr.nwname == 0 && nf != nil)
743
		return nil;
744
 
745
	/* walk each element */
746
	rv = nil;
747
	for(i = 0; i < thdr.nwname; i++){
748
		rv = dowalk(f, thdr.wname[i]);
749
		if(rv != nil){
750
			if(nf != nil)	
751
				rclunk(nf);
752
			break;
753
		}
754
		rhdr.wqid[i] = f->qid;
755
	}
756
	rhdr.nwqid = i;
757
 
758
	/* we only error out if no walk  */
759
	if(i > 0)
760
		rv = nil;
761
 
762
	return rv;
763
}
764
 
765
char *
766
ropen(Fid *f)
767
{
768
	int file;
769
 
770
	if(f->open)
771
		return Eisopen;
772
 
773
	file = FILE(f->qid.path);
774
	if(thdr.mode != OREAD)
775
		if(file != Qctl && file != Qmboxctl)
776
			return Eperm;
777
 
778
	// make sure we've decoded
779
	if(file == Qbody){
780
		if(f->m->decoded == 0)
781
			decode(f->m);
782
		if(f->m->converted == 0)
783
			convert(f->m);
784
	}
785
 
786
	rhdr.iounit = 0;
787
	rhdr.qid = f->qid;
788
	f->open = 1;
789
	return 0;
790
}
791
 
792
char *
793
rcreate(Fid*)
794
{
795
	return Eperm;
796
}
797
 
798
int
799
readtopdir(Fid*, uchar *buf, long off, int cnt, int blen)
800
{
801
	Dir d;
802
	int m, n;
803
	long pos;
804
	Mailbox *mb;
805
 
806
	n = 0;
807
	pos = 0;
808
	mkstat(&d, nil, nil, Qctl);
809
	m = convD2M(&d, &buf[n], blen);
810
	if(off <= pos){
811
		if(m <= BIT16SZ || m > cnt)
812
			return 0;
813
		n += m;
814
		cnt -= m;
815
	}
816
	pos += m;
817
 
818
	for(mb = mbl; mb != nil; mb = mb->next){
819
		mkstat(&d, mb, nil, Qmbox);
820
		m = convD2M(&d, &buf[n], blen-n);
821
		if(off <= pos){
822
			if(m <= BIT16SZ || m > cnt)
823
				break;
824
			n += m;
825
			cnt -= m;
826
		}
827
		pos += m;
828
	}
829
	return n;
830
}
831
 
832
int
833
readmboxdir(Fid *f, uchar *buf, long off, int cnt, int blen)
834
{
835
	Dir d;
836
	int n, m;
837
	long pos;
838
	Message *msg;
839
 
840
	n = 0;
841
	if(f->mb->ctl){
842
		mkstat(&d, f->mb, nil, Qmboxctl);
843
		m = convD2M(&d, &buf[n], blen);
844
		if(off == 0){
845
			if(m <= BIT16SZ || m > cnt){
846
				f->fptr = nil;
847
				return 0;
848
			}
849
			n += m;
850
			cnt -= m;
851
		} else
852
			off -= m;
853
	}
854
 
855
	// to avoid n**2 reads of the directory, use a saved finger pointer
856
	if(f->mb->vers == f->fvers && off >= f->foff && f->fptr != nil){
857
		msg = f->fptr;
858
		pos = f->foff;
859
	} else {
860
		msg = f->mb->root->part;
861
		pos = 0;
862
	} 
863
 
864
	for(; cnt > 0 && msg != nil; msg = msg->next){
865
		// act like deleted files aren't there
866
		if(msg->deleted)
867
			continue;
868
 
869
		mkstat(&d, f->mb, msg, Qdir);
870
		m = convD2M(&d, &buf[n], blen-n);
871
		if(off <= pos){
872
			if(m <= BIT16SZ || m > cnt)
873
				break;
874
			n += m;
875
			cnt -= m;
876
		}
877
		pos += m;
878
	}
879
 
880
	// save a finger pointer for next read of the mbox directory
881
	f->foff = pos;
882
	f->fptr = msg;
883
	f->fvers = f->mb->vers;
884
 
885
	return n;
886
}
887
 
888
int
889
readmsgdir(Fid *f, uchar *buf, long off, int cnt, int blen)
890
{
891
	Dir d;
892
	int i, n, m;
893
	long pos;
894
	Message *msg;
895
 
896
	n = 0;
897
	pos = 0;
898
	for(i = 0; i < Qmax; i++){
899
		mkstat(&d, f->mb, f->m, i);
900
		m = convD2M(&d, &buf[n], blen-n);
901
		if(off <= pos){
902
			if(m <= BIT16SZ || m > cnt)
903
				return n;
904
			n += m;
905
			cnt -= m;
906
		}
907
		pos += m;
908
	}
909
	for(msg = f->m->part; msg != nil; msg = msg->next){
910
		mkstat(&d, f->mb, msg, Qdir);
911
		m = convD2M(&d, &buf[n], blen-n);
912
		if(off <= pos){
913
			if(m <= BIT16SZ || m > cnt)
914
				break;
915
			n += m;
916
			cnt -= m;
917
		}
918
		pos += m;
919
	}
920
 
921
	return n;
922
}
923
 
924
char*
925
rread(Fid *f)
926
{
927
	long off;
928
	int t, i, n, cnt;
929
	char *p;
930
 
931
	rhdr.count = 0;
932
	off = thdr.offset;
933
	cnt = thdr.count;
934
 
935
	if(cnt > messagesize - IOHDRSZ)
936
		cnt = messagesize - IOHDRSZ;
937
 
938
	rhdr.data = (char*)mbuf;
939
 
940
	t = FILE(f->qid.path);
941
	if(f->qid.type & QTDIR){
942
		if(t == Qtop) {
943
			qlock(&mbllock);
944
			n = readtopdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
945
			qunlock(&mbllock);
946
		} else if(t == Qmbox) {
947
			qlock(f->mb);
948
			if(off == 0)
949
				syncmbox(f->mb, 1);
950
			n = readmboxdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
951
			qunlock(f->mb);
952
		} else if(t == Qmboxctl) {
953
			n = 0;
954
		} else {
955
			n = readmsgdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
956
		}
957
 
958
		rhdr.count = n;
959
		return nil;
960
	}
961
 
962
	if(FILE(f->qid.path) == Qheader){
963
		rhdr.count = readheader(f->m, (char*)mbuf, off, cnt);
964
		return nil;
965
	}
966
 
967
	if(FILE(f->qid.path) == Qinfo){
968
		rhdr.count = readinfo(f->m, (char*)mbuf, off, cnt);
969
		return nil;
970
	}
971
 
972
	i = fileinfo(f->m, FILE(f->qid.path), &p);
973
	if(off < i){
974
		if((off + cnt) > i)
975
			cnt = i - off;
976
		memmove(mbuf, p + off, cnt);
977
		rhdr.count = cnt;
978
	}
979
	return nil;
980
}
981
 
982
char*
983
rwrite(Fid *f)
984
{
985
	char *err;
986
	char *token[1024];
987
	int t, n;
988
	String *file;
989
 
990
	t = FILE(f->qid.path);
991
	rhdr.count = thdr.count;
992
	switch(t){
993
	case Qctl:
994
		if(thdr.count == 0)
995
			return Ebadctl;
996
		if(thdr.data[thdr.count-1] == '\n')
997
			thdr.data[thdr.count-1] = 0;
998
		else
999
			thdr.data[thdr.count] = 0;
1000
		n = tokenize(thdr.data, token, nelem(token));
1001
		if(n == 0)
1002
			return Ebadctl;
1003
		if(strcmp(token[0], "open") == 0){
1004
			file = s_new();
1005
			switch(n){
1006
			case 1:
1007
				err = Ebadctl;
1008
				break;
1009
			case 2:
1010
				mboxpath(token[1], getlog(), file, 0);
1011
				err = newmbox(s_to_c(file), nil, 0);
1012
				break;
1013
			default:
1014
				mboxpath(token[1], getlog(), file, 0);
1015
				if(strchr(token[2], '/') != nil)
1016
					err = "/ not allowed in mailbox name";
1017
				else
1018
					err = newmbox(s_to_c(file), token[2], 0);
1019
				break;
1020
			}
1021
			s_free(file);
1022
			return err;
1023
		}
1024
		if(strcmp(token[0], "close") == 0){
1025
			if(n < 2)
1026
				return nil;
1027
			freembox(token[1]);
1028
			return nil;
1029
		}
1030
		if(strcmp(token[0], "delete") == 0){
1031
			if(n < 3)
1032
				return nil;
1033
			delmessages(n-1, &token[1]);
1034
			return nil;
1035
		}
1036
		return Ebadctl;
1037
	case Qmboxctl:
1038
		if(f->mb && f->mb->ctl){
1039
			if(thdr.count == 0)
1040
				return Ebadctl;
1041
			if(thdr.data[thdr.count-1] == '\n')
1042
				thdr.data[thdr.count-1] = 0;
1043
			else
1044
				thdr.data[thdr.count] = 0;
1045
			n = tokenize(thdr.data, token, nelem(token));
1046
			if(n == 0)
1047
				return Ebadctl;
1048
			return (*f->mb->ctl)(f->mb, n, token);
1049
		}
1050
	}
1051
	return Eperm;
1052
}
1053
 
1054
char *
1055
rclunk(Fid *f)
1056
{
1057
	Mailbox *mb;
1058
 
1059
	f->busy = 0;
1060
	f->open = 0;
1061
	if(f->mtop != nil){
1062
		qlock(f->mb);
1063
		msgdecref(f->mb, f->mtop);
1064
		qunlock(f->mb);
1065
	}
1066
	f->m = f->mtop = nil;
1067
	mb = f->mb;
1068
	if(mb != nil){
1069
		f->mb = nil;
1070
		assert(mb->refs > 0);
1071
		qlock(&mbllock);
1072
		mboxdecref(mb);
1073
		qunlock(&mbllock);
1074
	}
1075
	f->fid = -1;
1076
	return 0;
1077
}
1078
 
1079
char *
1080
rremove(Fid *f)
1081
{
1082
	if(f->m != nil){
1083
		if(f->m->deleted == 0)
1084
			mailplumb(f->mb, f->m, 1);
1085
		f->m->deleted = 1;
1086
	}
1087
	return rclunk(f);
1088
}
1089
 
1090
char *
1091
rstat(Fid *f)
1092
{
1093
	Dir d;
1094
 
1095
	if(FILE(f->qid.path) == Qmbox){
1096
		qlock(f->mb);
1097
		syncmbox(f->mb, 1);
1098
		qunlock(f->mb);
1099
	}
1100
	mkstat(&d, f->mb, f->m, FILE(f->qid.path));
1101
	rhdr.nstat = convD2M(&d, mbuf, messagesize - IOHDRSZ);
1102
	rhdr.stat = mbuf;
1103
	return 0;
1104
}
1105
 
1106
char *
1107
rwstat(Fid*)
1108
{
1109
	return Eperm;
1110
}
1111
 
1112
Fid *
1113
newfid(int fid)
1114
{
1115
	Fid *f, *ff;
1116
 
1117
	ff = 0;
1118
	for(f = fids; f; f = f->next)
1119
		if(f->fid == fid)
1120
			return f;
1121
		else if(!ff && !f->busy)
1122
			ff = f;
1123
	if(ff){
1124
		ff->fid = fid;
1125
		ff->fptr = nil;
1126
		return ff;
1127
	}
1128
	f = emalloc(sizeof *f);
1129
	f->fid = fid;
1130
	f->fptr = nil;
1131
	f->next = fids;
1132
	fids = f;
1133
	return f;
1134
}
1135
 
1136
int
1137
fidmboxrefs(Mailbox *mb)
1138
{
1139
	Fid *f;
1140
	int refs = 0;
1141
 
1142
	for(f = fids; f; f = f->next){
1143
		if(f->mb == mb)
1144
			refs++;
1145
	}
1146
	return refs;
1147
}
1148
 
1149
void
1150
io(void)
1151
{
1152
	char *err;
1153
	int n;
1154
 
1155
	/* start a process to watch the mailboxes*/
1156
	if(plumbing){
1157
		switch(rfork(RFPROC|RFMEM)){
1158
		case -1:
1159
			/* oh well */
1160
			break;
1161
		case 0:
1162
			reader();
1163
			exits(nil);
1164
		default:
1165
			break;
1166
		}
1167
	}
1168
 
1169
	for(;;){
1170
		/*
1171
		 * reading from a pipe or a network device
1172
		 * will give an error after a few eof reads
1173
		 * however, we cannot tell the difference
1174
		 * between a zero-length read and an interrupt
1175
		 * on the processes writing to us,
1176
		 * so we wait for the error
1177
		 */
1178
		checkmboxrefs();
1179
		n = read9pmsg(mfd[0], mdata, messagesize);
1180
		if(n == 0)
1181
			continue;
1182
		if(n < 0)
1183
			return;
1184
		if(convM2S(mdata, n, &thdr) == 0)
1185
			continue;
1186
 
1187
		if(debug)
1188
			fprint(2, "%s:<-%F\n", argv0, &thdr);
1189
 
1190
		rhdr.data = (char*)mdata + messagesize;
1191
		if(!fcalls[thdr.type])
1192
			err = "bad fcall type";
1193
		else
1194
			err = (*fcalls[thdr.type])(newfid(thdr.fid));
1195
		if(err){
1196
			rhdr.type = Rerror;
1197
			rhdr.ename = err;
1198
		}else{
1199
			rhdr.type = thdr.type + 1;
1200
			rhdr.fid = thdr.fid;
1201
		}
1202
		rhdr.tag = thdr.tag;
1203
		if(debug)
1204
			fprint(2, "%s:->%F\n", argv0, &rhdr);/**/
1205
		n = convS2M(&rhdr, mdata, messagesize);
1206
		if(write(mfd[1], mdata, n) != n)
1207
			error("mount write");
1208
	}
1209
}
1210
 
1211
void
1212
reader(void)
1213
{
1214
	ulong t;
1215
	Dir *d;
1216
	Mailbox *mb;
1217
 
1218
	sleep(15*1000);
1219
	for(;;){
1220
		t = time(0);
1221
		qlock(&mbllock);
1222
		for(mb = mbl; mb != nil; mb = mb->next){
1223
			assert(mb->refs > 0);
1224
			if(mb->waketime != 0 && t > mb->waketime){
1225
				qlock(mb);
1226
				mb->waketime = 0;
1227
				break;
1228
			}
1229
 
1230
			d = dirstat(mb->path);
1231
			if(d == nil)
1232
				continue;
1233
 
1234
			qlock(mb);
1235
			if(mb->d)
1236
			if(d->qid.path != mb->d->qid.path
1237
			   || d->qid.vers != mb->d->qid.vers){
1238
				free(d);
1239
				break;
1240
			}
1241
			qunlock(mb);
1242
			free(d);
1243
		}
1244
		qunlock(&mbllock);
1245
		if(mb != nil){
1246
			syncmbox(mb, 1);
1247
			qunlock(mb);
1248
		} else
1249
			sleep(15*1000);
1250
	}
1251
}
1252
 
1253
int
1254
newid(void)
1255
{
1256
	int rv;
1257
	static int id;
1258
	static Lock idlock;
1259
 
1260
	lock(&idlock);
1261
	rv = ++id;
1262
	unlock(&idlock);
1263
 
1264
	return rv;
1265
}
1266
 
1267
void
1268
error(char *s)
1269
{
1270
	postnote(PNGROUP, getpid(), "die yankee pig dog");
1271
	fprint(2, "%s: %s: %r\n", argv0, s);
1272
	exits(s);
1273
}
1274
 
1275
 
1276
typedef struct Ignorance Ignorance;
1277
struct Ignorance
1278
{
1279
	Ignorance *next;
1280
	char	*str;		/* string */
1281
	int	partial;	/* true if not exact match */
1282
};
1283
Ignorance *ignorance;
1284
 
1285
/*
1286
 *  read the file of headers to ignore 
1287
 */
1288
void
1289
readignore(void)
1290
{
1291
	char *p;
1292
	Ignorance *i;
1293
	Biobuf *b;
1294
 
1295
	if(ignorance != nil)
1296
		return;
1297
 
1298
	b = Bopen("/mail/lib/ignore", OREAD);
1299
	if(b == 0)
1300
		return;
1301
	while(p = Brdline(b, '\n')){
1302
		p[Blinelen(b)-1] = 0;
1303
		while(*p && (*p == ' ' || *p == '\t'))
1304
			p++;
1305
		if(*p == '#')
1306
			continue;
1307
		i = malloc(sizeof(Ignorance));
1308
		if(i == 0)
1309
			break;
1310
		i->partial = strlen(p);
1311
		i->str = strdup(p);
1312
		if(i->str == 0){
1313
			free(i);
1314
			break;
1315
		}
1316
		i->next = ignorance;
1317
		ignorance = i;
1318
	}
1319
	Bterm(b);
1320
}
1321
 
1322
int
1323
ignore(char *p)
1324
{
1325
	Ignorance *i;
1326
 
1327
	readignore();
1328
	for(i = ignorance; i != nil; i = i->next)
1329
		if(cistrncmp(i->str, p, i->partial) == 0)
1330
			return 1;
1331
	return 0;
1332
}
1333
 
1334
int
1335
hdrlen(char *p, char *e)
1336
{
1337
	char *ep;
1338
 
1339
	ep = p;
1340
	do {
1341
		ep = strchr(ep, '\n');
1342
		if(ep == nil){
1343
			ep = e;
1344
			break;
1345
		}
1346
		ep++;
1347
		if(ep >= e){
1348
			ep = e;
1349
			break;
1350
		}
1351
	} while(*ep == ' ' || *ep == '\t');
1352
	return ep - p;
1353
}
1354
 
1355
// rfc2047 non-ascii: =?charset?q?encoded-text?=
1356
int
1357
rfc2047convert(String *s, char *token, int len)
1358
{
1359
	char charset[100], decoded[1024], *e, *x;
1360
	int l;
1361
 
1362
	if(len == 0)
1363
		return -1;
1364
 
1365
	e = token+len-2;
1366
	token += 2;
1367
 
1368
	x = memchr(token, '?', e-token);
1369
	if(x == nil || (l=x-token) >= sizeof charset)
1370
		return -1;
1371
	memmove(charset, token, l);
1372
	charset[l] = 0;
1373
 
1374
	token = x+1;
1375
 
1376
	// bail if it doesn't fit 
1377
	if(e-token > sizeof(decoded)-1)
1378
		return -1;
1379
 
1380
	// bail if we don't understand the encoding
1381
	if(cistrncmp(token, "b?", 2) == 0){
1382
		token += 2;
1383
		len = dec64((uchar*)decoded, sizeof(decoded), token, e-token);
1384
		decoded[len] = 0;
1385
	} else if(cistrncmp(token, "q?", 2) == 0){
1386
		token += 2;
1387
		len = decquoted(decoded, token, e, 1);
1388
		if(len > 0 && decoded[len-1] == '\n')
1389
			len--;
1390
		decoded[len] = 0;
1391
	} else
1392
		return -1;
1393
 
1394
	if(xtoutf(charset, &x, decoded, decoded+len) <= 0)
1395
		s_append(s, decoded);
1396
	else {
1397
		s_append(s, x);
1398
		free(x);
1399
	}
1400
	return 0;
1401
}
1402
 
1403
char*
1404
rfc2047start(char *start, char *end)
1405
{
1406
	int quests;
1407
 
1408
	if(*--end != '=')
1409
		return nil;
1410
	if(*--end != '?')
1411
		return nil;
1412
 
1413
	quests = 0;
1414
	for(end--; end >= start; end--){
1415
		switch(*end){
1416
		case '=':
1417
			if(quests == 3 && *(end+1) == '?')
1418
				return end;
1419
			break;
1420
		case '?':
1421
			++quests;
1422
			break;
1423
		case ' ':
1424
		case '\t':
1425
		case '\n':
1426
		case '\r':
1427
			/* can't have white space in a token */
1428
			return nil;
1429
		}
1430
	}
1431
	return nil;
1432
}
1433
 
1434
// convert a header line
1435
String*
1436
stringconvert(String *s, char *uneaten, int len)
1437
{
1438
	char *token, *p, *e;
1439
 
1440
	s = s_reset(s);
1441
	p = uneaten;
1442
	for(e = p+len; p < e; ){
1443
		while(*p++ == '=' && (token = rfc2047start(uneaten, p))){
1444
			s_nappend(s, uneaten, token-uneaten);
1445
			if(rfc2047convert(s, token, p - token) < 0)
1446
				s_nappend(s, token, p - token);
1447
			uneaten = p;
1448
			for(; p<e && isspace(*p);)
1449
				p++;
1450
			if(p+2 < e && p[0] == '=' && p[1] == '?')
1451
				uneaten = p;	// paste
1452
		}
1453
	}
1454
	if(p > uneaten)
1455
		s_nappend(s, uneaten, p-uneaten);
1456
	return s;
1457
}
1458
 
1459
int
1460
readheader(Message *m, char *buf, int off, int cnt)
1461
{
1462
	char *p, *e;
1463
	int n, ns;
1464
	char *to = buf;
1465
	String *s;
1466
 
1467
	p = m->header;
1468
	e = m->hend;
1469
	s = nil;
1470
 
1471
	// copy in good headers
1472
	while(cnt > 0 && p < e){
1473
		n = hdrlen(p, e);
1474
		if(ignore(p)){
1475
			p += n;
1476
			continue;
1477
		}
1478
 
1479
		// rfc2047 processing
1480
		s = stringconvert(s, p, n);
1481
		ns = s_len(s);
1482
		if(off > 0){
1483
			if(ns <= off){
1484
				off -= ns;
1485
				p += n;
1486
				continue;
1487
			}
1488
			ns -= off;
1489
		}
1490
		if(ns > cnt)
1491
			ns = cnt;
1492
		memmove(to, s_to_c(s)+off, ns);
1493
		to += ns;
1494
		p += n;
1495
		cnt -= ns;
1496
		off = 0;
1497
	}
1498
 
1499
	s_free(s);
1500
	return to - buf;
1501
}
1502
 
1503
int
1504
headerlen(Message *m)
1505
{
1506
	char buf[1024];
1507
	int i, n;
1508
 
1509
	if(m->hlen >= 0)
1510
		return m->hlen;
1511
	for(n = 0; ; n += i){
1512
		i = readheader(m, buf, n, sizeof(buf));
1513
		if(i <= 0)
1514
			break;
1515
	}
1516
	m->hlen = n;
1517
	return n;
1518
}
1519
 
1520
QLock hashlock;
1521
 
1522
uint
1523
hash(ulong ppath, char *name)
1524
{
1525
	uchar *p;
1526
	uint h;
1527
 
1528
	h = 0;
1529
	for(p = (uchar*)name; *p; p++)
1530
		h = h*7 + *p;
1531
	h += ppath;
1532
 
1533
	return h % Hsize;
1534
}
1535
 
1536
Hash*
1537
hlook(ulong ppath, char *name)
1538
{
1539
	int h;
1540
	Hash *hp;
1541
 
1542
	qlock(&hashlock);
1543
	h = hash(ppath, name);
1544
	for(hp = htab[h]; hp != nil; hp = hp->next)
1545
		if(ppath == hp->ppath && strcmp(name, hp->name) == 0){
1546
			qunlock(&hashlock);
1547
			return hp;
1548
		}
1549
	qunlock(&hashlock);
1550
	return nil;
1551
}
1552
 
1553
void
1554
henter(ulong ppath, char *name, Qid qid, Message *m, Mailbox *mb)
1555
{
1556
	int h;
1557
	Hash *hp, **l;
1558
 
1559
	qlock(&hashlock);
1560
	h = hash(ppath, name);
1561
	for(l = &htab[h]; *l != nil; l = &(*l)->next){
1562
		hp = *l;
1563
		if(ppath == hp->ppath && strcmp(name, hp->name) == 0){
1564
			hp->m = m;
1565
			hp->mb = mb;
1566
			hp->qid = qid;
1567
			qunlock(&hashlock);
1568
			return;
1569
		}
1570
	}
1571
 
1572
	*l = hp = emalloc(sizeof(*hp));
1573
	hp->m = m;
1574
	hp->mb = mb;
1575
	hp->qid = qid;
1576
	hp->name = name;
1577
	hp->ppath = ppath;
1578
	qunlock(&hashlock);
1579
}
1580
 
1581
void
1582
hfree(ulong ppath, char *name)
1583
{
1584
	int h;
1585
	Hash *hp, **l;
1586
 
1587
	qlock(&hashlock);
1588
	h = hash(ppath, name);
1589
	for(l = &htab[h]; *l != nil; l = &(*l)->next){
1590
		hp = *l;
1591
		if(ppath == hp->ppath && strcmp(name, hp->name) == 0){
1592
			hp->mb = nil;
1593
			*l = hp->next;
1594
			free(hp);
1595
			break;
1596
		}
1597
	}
1598
	qunlock(&hashlock);
1599
}
1600
 
1601
int
1602
hashmboxrefs(Mailbox *mb)
1603
{
1604
	int h;
1605
	Hash *hp;
1606
	int refs = 0;
1607
 
1608
	qlock(&hashlock);
1609
	for(h = 0; h < Hsize; h++){
1610
		for(hp = htab[h]; hp != nil; hp = hp->next)
1611
			if(hp->mb == mb)
1612
				refs++;
1613
	}
1614
	qunlock(&hashlock);
1615
	return refs;
1616
}
1617
 
1618
void
1619
checkmboxrefs(void)
1620
{
1621
	int f, refs;
1622
	Mailbox *mb;
1623
 
1624
	qlock(&mbllock);
1625
	for(mb=mbl; mb; mb=mb->next){
1626
		qlock(mb);
1627
		refs = (f=fidmboxrefs(mb))+1;
1628
		if(refs != mb->refs){
1629
			fprint(2, "mbox %s %s ref mismatch actual %d (%d+1) expected %d\n", mb->name, mb->path, refs, f, mb->refs);
1630
			abort();
1631
		}
1632
		qunlock(mb);
1633
	}
1634
	qunlock(&mbllock);
1635
}
1636
 
1637
void
1638
post(char *name, char *envname, int srvfd)
1639
{
1640
	int fd;
1641
	char buf[32];
1642
 
1643
	fd = create(name, OWRITE, 0600);
1644
	if(fd < 0)
1645
		error("post failed");
1646
	sprint(buf, "%d",srvfd);
1647
	if(write(fd, buf, strlen(buf)) != strlen(buf))
1648
		error("srv write");
1649
	close(fd);
1650
	putenv(envname, name);
1651
}