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-vt/sys/src/cmd/ip/imap4d/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 <u.h>
2
#include <libc.h>
3
#include <bio.h>
4
#include <auth.h>
5
#include "imap4d.h"
6
 
7
static NamedInt	flagChars[NFlags] =
8
{
9
	{"s",	MSeen},
10
	{"a",	MAnswered},
11
	{"f",	MFlagged},
12
	{"D",	MDeleted},
13
	{"d",	MDraft},
14
	{"r",	MRecent},
15
};
16
 
17
static	int	fsCtl = -1;
18
 
19
static	void	boxFlags(Box *box);
20
static	int	createImp(Box *box, Qid *qid);
21
static	void	fsInit(void);
22
static	void	mboxGone(Box *box);
23
static	MbLock	*openImp(Box *box, int new);
24
static	int	parseImp(Biobuf *b, Box *box);
25
static	int	readBox(Box *box);
26
static	ulong	uidRenumber(Msg *m, ulong uid, int force);
27
static	int	impFlags(Box *box, Msg *m, char *flags);
28
 
29
/*
30
 * strategy:
31
 * every mailbox file has an associated .imp file
32
 * which maps upas/fs message digests to uids & message flags.
33
 *
34
 * the .imp files are locked by /mail/fs/usename/L.mbox.
35
 * whenever the flags can be modified, the lock file
36
 * should be opened, thereby locking the uid & flag state.
37
 * for example, whenever new uids are assigned to messages,
38
 * and whenever flags are changed internally, the lock file
39
 * should be open and locked.  this means the file must be
40
 * opened during store command, and when changing the \seen
41
 * flag for the fetch command.
42
 *
43
 * if no .imp file exists, a null one must be created before
44
 * assigning uids.
45
 *
46
 * the .imp file has the following format
47
 * imp		: "imap internal mailbox description\n"
48
 * 			uidvalidity " " uidnext "\n"
49
 *			messageLines
50
 *
51
 * messageLines	:
52
 *		| messageLines digest " " uid " " flags "\n"
53
 *
54
 * uid, uidnext, and uidvalidity are 32 bit decimal numbers
55
 * printed right justified in a field NUid characters long.
56
 * the 0 uid implies that no uid has been assigned to the message,
57
 * but the flags are valid. note that message lines are in mailbox
58
 * order, except possibly for 0 uid messages.
59
 *
60
 * digest is an ascii hex string NDigest characters long.
61
 *
62
 * flags has a character for each of NFlag flag fields.
63
 * if the flag is clear, it is represented by a "-".
64
 * set flags are represented as a unique single ascii character.
65
 * the currently assigned flags are, in order:
66
 *	MSeen		s
67
 *	MAnswered	a
68
 *	MFlagged	f
69
 *	MDeleted	D
70
 *	MDraft		d
71
 */
72
Box*
73
openBox(char *name, char *fsname, int writable)
74
{
75
	Box *box;
76
	MbLock *ml;
77
	int n, new;
78
 
79
	if(cistrcmp(name, "inbox") == 0)
80
		if(access("msgs", AEXIST) == 0)
81
			name = "msgs";
82
		else
83
			name = "mbox";
84
	fsInit();
85
	debuglog("imap4d open %s %s\n", name, fsname);
86
 
87
	if(fprint(fsCtl, "open '/mail/box/%s/%s' %s", username, name, fsname) < 0){
88
//ZZZ
89
		char err[ERRMAX];
90
 
91
		rerrstr(err, sizeof err);
92
		if(strstr(err, "file does not exist") == nil)
93
			fprint(2,
94
		"imap4d at %lud: upas/fs open %s/%s as %s failed: '%s' %s",
95
			time(nil), username, name, fsname, err,
96
			ctime(time(nil)));  /* NB: ctime result ends with \n */
97
		fprint(fsCtl, "close %s", fsname);
98
		return nil;
99
	}
100
 
101
	/*
102
	 * read box to find all messages
103
	 * each one has a directory, and is in numerical order
104
	 */
105
	box = MKZ(Box);
106
	box->writable = writable;
107
 
108
	n = strlen(name) + 1;
109
	box->name = emalloc(n);
110
	strcpy(box->name, name);
111
 
112
	n += STRLEN(".imp");
113
	box->imp = emalloc(n);
114
	snprint(box->imp, n, "%s.imp", name);
115
 
116
	n = strlen(fsname) + 1;
117
	box->fs = emalloc(n);
118
	strcpy(box->fs, fsname);
119
 
120
	n = STRLEN("/mail/fs/") + strlen(fsname) + 1;
121
	box->fsDir = emalloc(n);
122
	snprint(box->fsDir, n, "/mail/fs/%s", fsname);
123
 
124
	box->uidnext = 1;
125
	new = readBox(box);
126
	if(new >= 0){
127
		ml = openImp(box, new);
128
		if(ml != nil){
129
			closeImp(box, ml);
130
			return box;
131
		}
132
	}
133
	closeBox(box, 0);
134
	return nil;
135
}
136
 
137
/*
138
 * check mailbox
139
 * returns fd of open .imp file if imped.
140
 * otherwise, return value is insignificant
141
 *
142
 * careful: called by idle polling proc
143
 */
144
MbLock*
145
checkBox(Box *box, int imped)
146
{
147
	MbLock *ml;
148
	Dir *d;
149
	int new;
150
 
151
	if(box == nil)
152
		return nil;
153
 
154
	/*
155
	 * if stat fails, mailbox must be gone
156
	 */
157
	d = cdDirstat(box->fsDir, ".");
158
	if(d == nil){
159
		mboxGone(box);
160
		return nil;
161
	}
162
	new = 0;
163
	if(box->qid.path != d->qid.path || box->qid.vers != d->qid.vers
164
	|| box->mtime != d->mtime){
165
		new = readBox(box);
166
		if(new < 0){
167
			free(d);
168
			return nil;
169
		}
170
	}
171
	free(d);
172
	ml = openImp(box, new);
173
	if(ml == nil)
174
		box->writable = 0;
175
	else if(!imped){
176
		closeImp(box, ml);
177
		ml = nil;
178
	}
179
	return ml;
180
}
181
 
182
/*
183
 * mailbox is unreachable, so mark all messages expunged
184
 * clean up .imp files as well.
185
 */
186
static void
187
mboxGone(Box *box)
188
{
189
	Msg *m;
190
 
191
	if(cdExists(mboxDir, box->name) < 0)
192
		cdRemove(mboxDir, box->imp);
193
	for(m = box->msgs; m != nil; m = m->next)
194
		m->expunged = 1;
195
	box->writable = 0;
196
}
197
 
198
/*
199
 * read messages in the mailbox
200
 * mark message that no longer exist as expunged
201
 * returns -1 for failure, 0 if no new messages, 1 if new messages.
202
 */
203
static int
204
readBox(Box *box)
205
{
206
	Msg *msgs, *m, *last;
207
	Dir *d;
208
	char *s;
209
	long max, id;
210
	int i, nd, fd, new;
211
 
212
	fd = cdOpen(box->fsDir, ".", OREAD);
213
	if(fd < 0){
214
		syslog(0, "mail",
215
		    "imap4d at %lud: upas/fs stat of %s/%s aka %s failed: %r",
216
			time(nil), username, box->name, box->fsDir);
217
		mboxGone(box);
218
		return -1;
219
	}
220
 
221
	/*
222
	 * read box to find all messages
223
	 * each one has a directory, and is in numerical order
224
	 */
225
	d = dirfstat(fd);
226
	if(d == nil){
227
		close(fd);
228
		return -1;
229
	}
230
	box->mtime = d->mtime;
231
	box->qid = d->qid;
232
	last = nil;
233
	msgs = box->msgs;
234
	max = 0;
235
	new = 0;
236
	free(d);
237
	while((nd = dirread(fd, &d)) > 0){
238
		for(i = 0; i < nd; i++){
239
			s = d[i].name;
240
			id = strtol(s, &s, 10);
241
			if(id <= max || *s != '\0'
242
			|| (d[i].mode & DMDIR) != DMDIR)
243
				continue;
244
 
245
			max = id;
246
 
247
			while(msgs != nil){
248
				last = msgs;
249
				msgs = msgs->next;
250
				if(last->id == id)
251
					goto continueDir;
252
				last->expunged = 1;
253
			}
254
 
255
			new = 1;
256
			m = MKZ(Msg);
257
			m->id = id;
258
			m->fsDir = box->fsDir;
259
			m->fs = emalloc(2 * (MsgNameLen + 1));
260
			m->efs = seprint(m->fs, m->fs + (MsgNameLen + 1), "%lud/", id);
261
			m->size = ~0UL;
262
			m->lines = ~0UL;
263
			m->prev = last;
264
			m->flags = MRecent;
265
			if(!msgInfo(m))
266
				freeMsg(m);
267
			else{
268
				if(last == nil)
269
					box->msgs = m;
270
				else
271
					last->next = m;
272
				last = m;
273
			}
274
	continueDir:;
275
		}
276
		free(d);
277
	}
278
	close(fd);
279
	for(; msgs != nil; msgs = msgs->next)
280
		msgs->expunged = 1;
281
 
282
	/*
283
	 * make up the imap message sequence numbers
284
	 */
285
	id = 1;
286
	for(m = box->msgs; m != nil; m = m->next){
287
		if(m->seq && m->seq != id)
288
			bye("internal error assigning message numbers");
289
		m->seq = id++;
290
	}
291
	box->max = id - 1;
292
 
293
	return new;
294
}
295
 
296
/*
297
 * read in the .imp file, or make one if it doesn't exist.
298
 * make sure all flags and uids are consistent.
299
 * return the mailbox lock.
300
 */
301
#define IMPMAGIC	"imap internal mailbox description\n"
302
static MbLock*
303
openImp(Box *box, int new)
304
{
305
	Qid qid;
306
	Biobuf b;
307
	MbLock *ml;
308
	int fd;
309
//ZZZZ
310
	int once;
311
 
312
	ml = mbLock();
313
	if(ml == nil)
314
		return nil;
315
	fd = cdOpen(mboxDir, box->imp, OREAD);
316
	once = 0;
317
ZZZhack:
318
	if(fd < 0 || fqid(fd, &qid) < 0){
319
		if(fd < 0){
320
			char buf[ERRMAX];
321
 
322
			errstr(buf, sizeof buf);
323
			if(cistrstr(buf, "does not exist") == nil)
324
				fprint(2, "imap4d at %lud: imp open failed: %s\n", time(nil), buf);
325
			if(!once && cistrstr(buf, "locked") != nil){
326
				once = 1;
327
				fprint(2, "imap4d at %lud: imp %s/%s %s locked when it shouldn't be; spinning\n", time(nil), username, box->name, box->imp);
328
				fd = openLocked(mboxDir, box->imp, OREAD);
329
				goto ZZZhack;
330
			}
331
		}
332
		if(fd >= 0)
333
			close(fd);
334
		fd = createImp(box, &qid);
335
		if(fd < 0){
336
			mbUnlock(ml);
337
			return nil;
338
		}
339
		box->dirtyImp = 1;
340
		if(box->uidvalidity == 0)
341
			box->uidvalidity = box->mtime;
342
		box->impQid = qid;
343
		new = 1;
344
	}else if(qid.path != box->impQid.path || qid.vers != box->impQid.vers){
345
		Binit(&b, fd, OREAD);
346
		if(!parseImp(&b, box)){
347
			box->dirtyImp = 1;
348
			if(box->uidvalidity == 0)
349
				box->uidvalidity = box->mtime;
350
		}
351
		Bterm(&b);
352
		box->impQid = qid;
353
		new = 1;
354
	}
355
	if(new)
356
		boxFlags(box);
357
	close(fd);
358
	return ml;
359
}
360
 
361
/*
362
 * close the .imp file, after writing out any changes
363
 */
364
void
365
closeImp(Box *box, MbLock *ml)
366
{
367
	Msg *m;
368
	Qid qid;
369
	Biobuf b;
370
	char buf[NFlags+1];
371
	int fd;
372
 
373
	if(ml == nil)
374
		return;
375
	if(!box->dirtyImp){
376
		mbUnlock(ml);
377
		return;
378
	}
379
 
380
	fd = cdCreate(mboxDir, box->imp, OWRITE, 0664);
381
	if(fd < 0){
382
		mbUnlock(ml);
383
		return;
384
	}
385
	Binit(&b, fd, OWRITE);
386
 
387
	box->dirtyImp = 0;
388
	Bprint(&b, "%s", IMPMAGIC);
389
	Bprint(&b, "%.*lud %.*lud\n", NUid, box->uidvalidity, NUid, box->uidnext);
390
	for(m = box->msgs; m != nil; m = m->next){
391
		if(m->expunged)
392
			continue;
393
		wrImpFlags(buf, m->flags, strcmp(box->fs, "imap") == 0);
394
		Bprint(&b, "%.*s %.*lud %s\n", NDigest, m->info[IDigest], NUid, m->uid, buf);
395
	}
396
	Bterm(&b);
397
 
398
	if(fqid(fd, &qid) >= 0)
399
		box->impQid = qid;
400
	close(fd);
401
	mbUnlock(ml);
402
}
403
 
404
void
405
wrImpFlags(char *buf, int flags, int killRecent)
406
{
407
	int i;
408
 
409
	for(i = 0; i < NFlags; i++){
410
		if((flags & flagChars[i].v)
411
		&& (flagChars[i].v != MRecent || !killRecent))
412
			buf[i] = flagChars[i].name[0];
413
		else
414
			buf[i] = '-';
415
	}
416
	buf[i] = '\0';
417
}
418
 
419
int
420
emptyImp(char *mbox)
421
{
422
	Dir *d;
423
	long mode;
424
	int fd;
425
 
426
	fd = cdCreate(mboxDir, impName(mbox), OWRITE, 0664);
427
	if(fd < 0)
428
		return -1;
429
	d = cdDirstat(mboxDir, mbox);
430
	if(d == nil){
431
		close(fd);
432
		return -1;
433
	}
434
	fprint(fd, "%s%.*lud %.*lud\n", IMPMAGIC, NUid, d->mtime, NUid, 1UL);
435
	mode = d->mode & 0777;
436
	nulldir(d);
437
	d->mode = mode;
438
	dirfwstat(fd, d);
439
	free(d);
440
	return fd;
441
}
442
 
443
/*
444
 * try to match permissions with mbox
445
 */
446
static int
447
createImp(Box *box, Qid *qid)
448
{
449
	Dir *d;
450
	long mode;
451
	int fd;
452
 
453
	fd = cdCreate(mboxDir, box->imp, OREAD, 0664);
454
	if(fd < 0)
455
		return -1;
456
	d = cdDirstat(mboxDir, box->name);
457
	if(d != nil){
458
		mode = d->mode & 0777;
459
		nulldir(d);
460
		d->mode = mode;
461
		dirfwstat(fd, d);
462
		free(d);
463
	}
464
	if(fqid(fd, qid) < 0){
465
		close(fd);
466
		return -1;
467
	}
468
 
469
	return fd;
470
}
471
 
472
/*
473
 * read or re-read a .imp file.
474
 * this is tricky:
475
 *	messages can be deleted by another agent
476
 *	we might still have a Msg for an expunged message,
477
 *		because we haven't told the client yet.
478
 *	we can have a Msg without a .imp entry.
479
 *	flag information is added at the end of the .imp by copy & append
480
 *	there can be duplicate messages (same digests).
481
 *
482
 * look up existing messages based on uid.
483
 * look up new messages based on in order digest matching.
484
 *
485
 * note: in the face of duplicate messages, one of which is deleted,
486
 * two active servers may decide different ones are valid, and so return
487
 * different uids for the messages.  this situation will stablize when the servers exit.
488
 */
489
static int
490
parseImp(Biobuf *b, Box *box)
491
{
492
	Msg *m, *mm;
493
	char *s, *t, *toks[3];
494
	ulong uid, u;
495
	int match, n;
496
 
497
	m = box->msgs;
498
	s = Brdline(b, '\n');
499
	if(s == nil || Blinelen(b) != STRLEN(IMPMAGIC)
500
	|| strncmp(s, IMPMAGIC, STRLEN(IMPMAGIC)) != 0)
501
		return 0;
502
 
503
	s = Brdline(b, '\n');
504
	if(s == nil || Blinelen(b) != 2*NUid + 2)
505
		return 0;
506
	s[2*NUid + 1] = '\0';
507
	u = strtoul(s, &t, 10);
508
	if(u != box->uidvalidity && box->uidvalidity != 0)
509
		return 0;
510
	box->uidvalidity = u;
511
	if(*t != ' ' || t != s + NUid)
512
		return 0;
513
	t++;
514
	u = strtoul(t, &t, 10);
515
	if(box->uidnext > u)
516
		return 0;
517
	box->uidnext = u;
518
	if(t != s + 2*NUid+1 || box->uidnext == 0)
519
		return 0;
520
 
521
	uid = ~0;
522
	while(m != nil){
523
		s = Brdline(b, '\n');
524
		if(s == nil)
525
			break;
526
		n = Blinelen(b) - 1;
527
		if(n != NDigest + NUid + NFlags + 2
528
		|| s[NDigest] != ' ' || s[NDigest + NUid + 1] != ' ')
529
			return 0;
530
		toks[0] = s;
531
		s[NDigest] = '\0';
532
		toks[1] = s + NDigest + 1;
533
		s[NDigest + NUid + 1] = '\0';
534
		toks[2] = s + NDigest + NUid + 2;
535
		s[n] = '\0';
536
		t = toks[1];
537
		u = strtoul(t, &t, 10);
538
		if(*t != '\0' || uid != ~0 && (uid >= u && u || u && !uid))
539
			return 0;
540
		uid = u;
541
 
542
		/*
543
		 * zero uid => added by append or copy, only flags valid
544
		 * can only match messages without uids, but this message
545
		 * may not be the next one, and may have been deleted.
546
		 */
547
		if(!uid){
548
			for(; m != nil && m->uid; m = m->next)
549
				;
550
			for(mm = m; mm != nil; mm = mm->next){
551
				if(mm->info[IDigest] != nil &&
552
				    strcmp(mm->info[IDigest], toks[0]) == 0){
553
					if(!mm->uid)
554
						mm->flags = 0;
555
					if(!impFlags(box, mm, toks[2]))
556
						return 0;
557
					m = mm->next;
558
					break;
559
				}
560
			}
561
			continue;
562
		}
563
 
564
		/*
565
		 * ignore expunged messages,
566
		 * and messages already assigned uids which don't match this uid.
567
		 * such messages must have been deleted by another imap server,
568
		 * which updated the mailbox and .imp file since we read the mailbox,
569
		 * or because upas/fs got confused by consecutive duplicate messages,
570
		 * the first of which was deleted by another imap server.
571
		 */
572
		for(; m != nil && (m->expunged || m->uid && m->uid < uid); m = m->next)
573
			;
574
		if(m == nil)
575
			break;
576
 
577
		/*
578
		 * only check for digest match on the next message,
579
		 * since it comes before all other messages, and therefore
580
		 * must be in the .imp file if they should be.
581
		 */
582
		match = m->info[IDigest] != nil &&
583
			strcmp(m->info[IDigest], toks[0]) == 0;
584
		if(uid && (m->uid == uid || !m->uid && match)){
585
			if(!match)
586
				bye("inconsistent uid");
587
 
588
			/*
589
			 * wipe out recent flag if some other server saw this new message.
590
			 * it will be read from the .imp file if is really should be set,
591
			 * ie the message was only seen by a status command.
592
			 */
593
			if(!m->uid)
594
				m->flags = 0;
595
 
596
			if(!impFlags(box, m, toks[2]))
597
				return 0;
598
			m->uid = uid;
599
			m = m->next;
600
		}
601
	}
602
	return 1;
603
}
604
 
605
/*
606
 * parse .imp flags
607
 */
608
static int
609
impFlags(Box *box, Msg *m, char *flags)
610
{
611
	int i, f;
612
 
613
	f = 0;
614
	for(i = 0; i < NFlags; i++){
615
		if(flags[i] == '-')
616
			continue;
617
		if(flags[i] != flagChars[i].name[0])
618
			return 0;
619
		f |= flagChars[i].v;
620
	}
621
 
622
	/*
623
	 * recent flags are set until the first time message's box is selected or examined.
624
	 * it may be stored in the file as a side effect of a status or subscribe command;
625
	 * if so, clear it out.
626
	 */
627
	if((f & MRecent) && strcmp(box->fs, "imap") == 0)
628
		box->dirtyImp = 1;
629
	f |= m->flags & MRecent;
630
 
631
	/*
632
	 * all old messages with changed flags should be reported to the client
633
	 */
634
	if(m->uid && m->flags != f){
635
		box->sendFlags = 1;
636
		m->sendFlags = 1;
637
	}
638
	m->flags = f;
639
	return 1;
640
}
641
 
642
/*
643
 * assign uids to any new messages
644
 * which aren't already in the .imp file.
645
 * sum up totals for flag values.
646
 */
647
static void
648
boxFlags(Box *box)
649
{
650
	Msg *m;
651
 
652
	box->recent = 0;
653
	for(m = box->msgs; m != nil; m = m->next){
654
		if(m->uid == 0){
655
			box->dirtyImp = 1;
656
			box->uidnext = uidRenumber(m, box->uidnext, 0);
657
		}
658
		if(m->flags & MRecent)
659
			box->recent++;
660
	}
661
}
662
 
663
static ulong
664
uidRenumber(Msg *m, ulong uid, int force)
665
{
666
	for(; m != nil; m = m->next){
667
		if(!force && m->uid != 0)
668
			bye("uid renumbering with a valid uid");
669
		m->uid = uid++;
670
	}
671
	return uid;
672
}
673
 
674
void
675
closeBox(Box *box, int opened)
676
{
677
	Msg *m, *next;
678
 
679
	/*
680
	 * make sure to leave the mailbox directory so upas/fs can close the mailbox
681
	 */
682
	myChdir(mboxDir);
683
 
684
	if(box->writable){
685
		deleteMsgs(box);
686
		if(expungeMsgs(box, 0))
687
			closeImp(box, checkBox(box, 1));
688
	}
689
 
690
	if(fprint(fsCtl, "close %s", box->fs) < 0 && opened)
691
		bye("can't talk to mail server");
692
	for(m = box->msgs; m != nil; m = next){
693
		next = m->next;
694
		freeMsg(m);
695
	}
696
	free(box->name);
697
	free(box->fs);
698
	free(box->fsDir);
699
	free(box->imp);
700
	free(box);
701
}
702
 
703
int
704
deleteMsgs(Box *box)
705
{
706
	Msg *m;
707
	char buf[BufSize], *p, *start;
708
	int ok;
709
 
710
	if(!box->writable)
711
		return 0;
712
 
713
	/*
714
	 * first pass: delete messages; gang the writes together for speed.
715
	 */
716
	ok = 1;
717
	start = seprint(buf, buf + sizeof(buf), "delete %s", box->fs);
718
	p = start;
719
	for(m = box->msgs; m != nil; m = m->next){
720
		if((m->flags & MDeleted) && !m->expunged){
721
			m->expunged = 1;
722
			p = seprint(p, buf + sizeof(buf), " %lud", m->id);
723
			if(p + 32 >= buf + sizeof(buf)){
724
				if(write(fsCtl, buf, p - buf) < 0)
725
					bye("can't talk to mail server");
726
				p = start;
727
			}
728
		}
729
	}
730
	if(p != start && write(fsCtl, buf, p - buf) < 0)
731
		bye("can't talk to mail server");
732
 
733
	return ok;
734
}
735
 
736
/*
737
 * second pass: remove the message structure,
738
 * and renumber message sequence numbers.
739
 * update messages counts in mailbox.
740
 * returns true if anything changed.
741
 */
742
int
743
expungeMsgs(Box *box, int send)
744
{
745
	Msg *m, *next, *last;
746
	ulong n;
747
 
748
	n = 0;
749
	last = nil;
750
	for(m = box->msgs; m != nil; m = next){
751
		m->seq -= n;
752
		next = m->next;
753
		if(m->expunged){
754
			if(send)
755
				Bprint(&bout, "* %lud expunge\r\n", m->seq);
756
			if(m->flags & MRecent)
757
				box->recent--;
758
			n++;
759
			if(last == nil)
760
				box->msgs = next;
761
			else
762
				last->next = next;
763
			freeMsg(m);
764
		}else
765
			last = m;
766
	}
767
	if(n){
768
		box->max -= n;
769
		box->dirtyImp = 1;
770
	}
771
	return n;
772
}
773
 
774
static void
775
fsInit(void)
776
{
777
	if(fsCtl >= 0)
778
		return;
779
	fsCtl = open("/mail/fs/ctl", ORDWR);
780
	if(fsCtl < 0)
781
		bye("can't open mail file system");
782
	if(fprint(fsCtl, "close mbox") < 0)
783
		bye("can't initialize mail file system");
784
}
785
 
786
static char *stoplist[] =
787
{
788
	"mbox",
789
	"pipeto",
790
	"forward",
791
	"names",
792
	"pipefrom",
793
	"headers",
794
	"imap.ok",
795
 
796
};
797
 
798
enum {
799
	Maxokbytes	= 4096,
800
	Maxfolders	= Maxokbytes / 4,
801
};
802
 
803
static char *folders[Maxfolders];
804
static char *folderbuff;
805
 
806
static void
807
readokfolders(void)
808
{
809
	int fd, nr;
810
 
811
	fd = open("imap.ok", OREAD);
812
	if(fd < 0)
813
		return;
814
	folderbuff = malloc(Maxokbytes);
815
	if(folderbuff == nil) {
816
		close(fd);
817
		return;
818
	}
819
	nr = read(fd, folderbuff, Maxokbytes-1);	/* once is ok */
820
	close(fd);
821
	if(nr < 0){
822
		free(folderbuff);
823
		folderbuff = nil;
824
		return;
825
	}
826
	folderbuff[nr] = 0;
827
	tokenize(folderbuff, folders, nelem(folders));
828
}
829
 
830
/*
831
 * reject bad mailboxes based on mailbox name
832
 */
833
int
834
okMbox(char *path)
835
{
836
	char *name;
837
	int i;
838
 
839
	if(folderbuff == nil && access("imap.ok", AREAD) == 0)
840
		readokfolders();
841
	name = strrchr(path, '/');
842
	if(name == nil)
843
		name = path;
844
	else
845
		name++;
846
	if(folderbuff != nil){
847
		for(i = 0; i < nelem(folders) && folders[i] != nil; i++)
848
			if(cistrcmp(folders[i], name) == 0)
849
				return 1;
850
		return 0;
851
	}
852
	if(strlen(name) + STRLEN(".imp") >= MboxNameLen)
853
		return 0;
854
	for(i = 0; stoplist[i]; i++)
855
		if(strcmp(name, stoplist[i]) == 0)
856
			return 0;
857
	if(isprefix("L.", name) || isprefix("imap-tmp.", name)
858
	|| issuffix(".imp", name)
859
	|| strcmp("imap.subscribed", name) == 0
860
	|| isdotdot(name) || name[0] == '/')
861
		return 0;
862
	return 1;
863
}