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_unix/acme/mail/src/mail.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 <thread.h>
5
#include <plumb.h>
6
#include <ctype.h>
7
#include "dat.h"
8
 
9
char	*maildir = "/mail/fs/";			/* mountpoint of mail file system */
10
char	*mailtermdir = "/mnt/term/mail/fs/";	/* alternate mountpoint */
11
char *mboxname = "mbox";			/* mailboxdir/mboxname is mail spool file */
12
char	*mailboxdir = nil;				/* nil == /mail/box/$user */
13
char *fsname;						/* filesystem for mailboxdir/mboxname is at maildir/fsname */
14
char	*user;
15
char	*outgoing;
16
 
17
Window	*wbox;
18
Message	mbox;
19
Message	replies;
20
char		*home;
21
int		plumbsendfd;
22
int		plumbseemailfd;
23
int		plumbshowmailfd;
24
int		plumbsendmailfd;
25
Channel	*cplumb;
26
Channel	*cplumbshow;
27
Channel	*cplumbsend;
28
int		wctlfd;
29
void		mainctl(void*);
30
void		plumbproc(void*);
31
void		plumbshowproc(void*);
32
void		plumbsendproc(void*);
33
void		plumbthread(void);
34
void		plumbshowthread(void*);
35
void		plumbsendthread(void*);
36
 
37
int			shortmenu;
38
 
39
void
40
usage(void)
41
{
42
	fprint(2, "usage: Mail [-sS] [-o outgoing] [mailboxname [directoryname]]\n");
43
	threadexitsall("usage");
44
}
45
 
46
void
47
removeupasfs(void)
48
{
49
	char buf[256];
50
 
51
	if(strcmp(mboxname, "mbox") == 0)
52
		return;
53
	snprint(buf, sizeof buf, "close %s", mboxname);
54
	write(mbox.ctlfd, buf, strlen(buf));
55
}
56
 
57
int
58
ismaildir(char *s)
59
{
60
	char buf[256];
61
	Dir *d;
62
	int ret;
63
 
64
	snprint(buf, sizeof buf, "%s%s", maildir, s);
65
	d = dirstat(buf);
66
	if(d == nil)
67
		return 0;
68
	ret = d->qid.type & QTDIR;
69
	free(d);
70
	return ret;
71
}
72
 
73
void
74
threadmain(int argc, char *argv[])
75
{
76
	char *s, *name;
77
	char err[ERRMAX], *cmd;
78
	int i, newdir;
79
	Fmt fmt;
80
 
81
	doquote = needsrcquote;
82
	quotefmtinstall();
83
 
84
	/* open these early so we won't miss notification of new mail messages while we read mbox */
85
	plumbsendfd = plumbopen("send", OWRITE|OCEXEC);
86
	plumbseemailfd = plumbopen("seemail", OREAD|OCEXEC);
87
	plumbshowmailfd = plumbopen("showmail", OREAD|OCEXEC);
88
 
89
	shortmenu = 0;
90
	ARGBEGIN{
91
	case 's':
92
		shortmenu = 1;
93
		break;
94
	case 'S':
95
		shortmenu = 2;
96
		break;
97
	case 'o':
98
		outgoing = EARGF(usage());
99
		break;
100
	case 'm':
101
		smprint(maildir, "%s/", EARGF(usage()));
102
		break;
103
	default:
104
		usage();
105
	}ARGEND
106
 
107
	name = "mbox";
108
 
109
	/* bind the terminal /mail/fs directory over the local one */
110
	if(access(maildir, 0)<0 && access(mailtermdir, 0)==0)
111
		bind(mailtermdir, maildir, MAFTER);
112
 
113
	newdir = 1;
114
	if(argc > 0){
115
		i = strlen(argv[0]);
116
		if(argc>2 || i==0)
117
			usage();
118
		/* see if the name is that of an existing /mail/fs directory */
119
		if(argc==1 && strchr(argv[0], '/')==0 && ismaildir(argv[0])){
120
			name = argv[0];
121
			mboxname = eappend(estrdup(maildir), "", name);
122
			newdir = 0;
123
		}else{
124
			if(argv[0][i-1] == '/')
125
				argv[0][i-1] = '\0';
126
			s = strrchr(argv[0], '/');
127
			if(s == nil)
128
				mboxname = estrdup(argv[0]);
129
			else{
130
				*s++ = '\0';
131
				if(*s == '\0')
132
					usage();
133
				mailboxdir = argv[0];
134
				mboxname = estrdup(s);
135
			}
136
			if(argc > 1)
137
				name = argv[1];
138
			else
139
				name = mboxname;
140
		}
141
	}
142
 
143
	user = getenv("user");
144
	if(user == nil)
145
		user = "none";
146
	if(mailboxdir == nil)
147
		mailboxdir = estrstrdup("/mail/box/", user);
148
	if(outgoing == nil)
149
		outgoing = estrstrdup(mailboxdir, "/outgoing");
150
 
151
	s = estrstrdup(maildir, "ctl");
152
	mbox.ctlfd = open(s, ORDWR|OCEXEC);
153
	if(mbox.ctlfd < 0)
154
		error("can't open %s: %r", s);
155
 
156
	fsname = estrdup(name);
157
	if(newdir && argc > 0){
158
		s = emalloc(5+strlen(mailboxdir)+strlen(mboxname)+strlen(name)+10+1);
159
		for(i=0; i<10; i++){
160
			sprint(s, "open %s/%s %s", mailboxdir, mboxname, fsname);
161
			if(write(mbox.ctlfd, s, strlen(s)) >= 0)
162
				break;
163
			err[0] = '\0';
164
			errstr(err, sizeof err);
165
			if(strstr(err, "mbox name in use") == nil)
166
				error("can't create directory %s for mail: %s", name, err);
167
			free(fsname);
168
			fsname = emalloc(strlen(name)+10);
169
			sprint(fsname, "%s-%d", name, i);
170
		}
171
		if(i == 10)
172
			error("can't open %s/%s: %r", mailboxdir, mboxname);
173
		free(s);
174
	}
175
 
176
	s = estrstrdup(fsname, "/");
177
	mbox.name = estrstrdup(maildir, s);
178
	mbox.level= 0;
179
	readmbox(&mbox, maildir, s);
180
	home = getenv("home");
181
	if(home == nil)
182
		home = "/";
183
 
184
	wbox = newwindow();
185
	winname(wbox, mbox.name);
186
	wintagwrite(wbox, "Put Mail Delmesg ", 3+1+4+1+7+1);
187
	threadcreate(mainctl, wbox, STACK);
188
 
189
	fmtstrinit(&fmt);
190
	fmtprint(&fmt, "Mail");
191
	if(shortmenu)
192
		fmtprint(&fmt, " -%c", "sS"[shortmenu-1]);
193
	if(outgoing)
194
		fmtprint(&fmt, " -o %s", outgoing);
195
	fmtprint(&fmt, " %s", name);
196
	cmd = fmtstrflush(&fmt);
197
	if(cmd == nil)
198
		sysfatal("out of memory");
199
	winsetdump(wbox, "/acme/mail", cmd);
200
	mbox.w = wbox;
201
 
202
	mesgmenu(wbox, &mbox);
203
	winclean(wbox);
204
 
205
	wctlfd = open("/dev/wctl", OWRITE|OCEXEC);	/* for acme window */
206
	cplumb = chancreate(sizeof(Plumbmsg*), 0);
207
	cplumbshow = chancreate(sizeof(Plumbmsg*), 0);
208
	if(strcmp(name, "mbox") == 0){
209
		/*
210
		 * Avoid creating multiple windows to send mail by only accepting
211
		 * sendmail plumb messages if we're reading the main mailbox.
212
		 */
213
		plumbsendmailfd = plumbopen("sendmail", OREAD|OCEXEC);
214
		cplumbsend = chancreate(sizeof(Plumbmsg*), 0);
215
		proccreate(plumbsendproc, nil, STACK);
216
		threadcreate(plumbsendthread, nil, STACK);
217
	}
218
	/* start plumb reader as separate proc ... */
219
	proccreate(plumbproc, nil, STACK);
220
	proccreate(plumbshowproc, nil, STACK);
221
	threadcreate(plumbshowthread, nil, STACK);
222
	/* ... and use this thread to read the messages */
223
	plumbthread();
224
}
225
 
226
void
227
plumbproc(void*)
228
{
229
	Plumbmsg *m;
230
 
231
	threadsetname("plumbproc");
232
	for(;;){
233
		m = plumbrecv(plumbseemailfd);
234
		sendp(cplumb, m);
235
		if(m == nil)
236
			threadexits(nil);
237
	}
238
}
239
 
240
void
241
plumbshowproc(void*)
242
{
243
	Plumbmsg *m;
244
 
245
	threadsetname("plumbshowproc");
246
	for(;;){
247
		m = plumbrecv(plumbshowmailfd);
248
		sendp(cplumbshow, m);
249
		if(m == nil)
250
			threadexits(nil);
251
	}
252
}
253
 
254
void
255
plumbsendproc(void*)
256
{
257
	Plumbmsg *m;
258
 
259
	threadsetname("plumbsendproc");
260
	for(;;){
261
		m = plumbrecv(plumbsendmailfd);
262
		sendp(cplumbsend, m);
263
		if(m == nil)
264
			threadexits(nil);
265
	}
266
}
267
 
268
void
269
newmesg(char *name, char *digest)
270
{
271
	Dir *d;
272
 
273
	if(strncmp(name, mbox.name, strlen(mbox.name)) != 0)
274
		return;	/* message is about another mailbox */
275
	if(mesglookupfile(&mbox, name, digest) != nil)
276
		return;
277
	d = dirstat(name);
278
	if(d == nil)
279
		return;
280
	if(mesgadd(&mbox, mbox.name, d, digest))
281
		mesgmenunew(wbox, &mbox);
282
	free(d);
283
}
284
 
285
void
286
showmesg(char *name, char *digest)
287
{
288
	char *n;
289
 
290
	if(strncmp(name, mbox.name, strlen(mbox.name)) != 0)
291
		return;	/* message is about another mailbox */
292
	n = estrdup(name+strlen(mbox.name));
293
	if(n[strlen(n)-1] != '/')
294
		n = egrow(n, "/", nil);
295
	mesgopen(&mbox, mbox.name, name+strlen(mbox.name), nil, 1, digest);
296
	free(n);
297
}
298
 
299
void
300
delmesg(char *name, char *digest, int dodel)
301
{
302
	Message *m;
303
 
304
	m = mesglookupfile(&mbox, name, digest);
305
	if(m != nil){
306
		mesgmenumarkdel(wbox, &mbox, m, 0);
307
		if(dodel)
308
			m->writebackdel = 1;
309
	}
310
}
311
 
312
void
313
plumbthread(void)
314
{
315
	Plumbmsg *m;
316
	Plumbattr *a;
317
	char *type, *digest;
318
 
319
	threadsetname("plumbthread");
320
	while((m = recvp(cplumb)) != nil){
321
		a = m->attr;
322
		digest = plumblookup(a, "digest");
323
		type = plumblookup(a, "mailtype");
324
		if(type == nil)
325
			fprint(2, "Mail: plumb message with no mailtype attribute\n");
326
		else if(strcmp(type, "new") == 0)
327
			newmesg(m->data, digest);
328
		else if(strcmp(type, "delete") == 0)
329
			delmesg(m->data, digest, 0);
330
		else
331
			fprint(2, "Mail: unknown plumb attribute %s\n", type);
332
		plumbfree(m);
333
	}
334
	threadexits(nil);
335
}
336
 
337
void
338
plumbshowthread(void*)
339
{
340
	Plumbmsg *m;
341
 
342
	threadsetname("plumbshowthread");
343
	while((m = recvp(cplumbshow)) != nil){
344
		showmesg(m->data, plumblookup(m->attr, "digest"));
345
		plumbfree(m);
346
	}
347
	threadexits(nil);
348
}
349
 
350
void
351
plumbsendthread(void*)
352
{
353
	Plumbmsg *m;
354
 
355
	threadsetname("plumbsendthread");
356
	while((m = recvp(cplumbsend)) != nil){
357
		mkreply(nil, "Mail", m->data, m->attr, nil);
358
		plumbfree(m);
359
	}
360
	threadexits(nil);
361
}
362
 
363
int
364
mboxcommand(Window *w, char *s)
365
{
366
	char *args[10], **targs;
367
	Message *m, *next;
368
	int ok, nargs, i, j;
369
	char buf[128];
370
 
371
	nargs = tokenize(s, args, nelem(args));
372
	if(nargs == 0)
373
		return 0;
374
	if(strcmp(args[0], "Mail") == 0){
375
		if(nargs == 1)
376
			mkreply(nil, "Mail", "", nil, nil);
377
		else
378
			mkreply(nil, "Mail", args[1], nil, nil);
379
		return 1;
380
	}
381
	if(strcmp(s, "Del") == 0){
382
		if(mbox.dirty){
383
			mbox.dirty = 0;
384
			fprint(2, "mail: mailbox not written\n");
385
			return 1;
386
		}
387
		ok = 1;
388
		for(m=mbox.head; m!=nil; m=next){
389
			next = m->next;
390
			if(m->w){
391
				if(windel(m->w, 0))
392
					m->w = nil;
393
				else
394
					ok = 0;
395
			}
396
		}
397
		for(m=replies.head; m!=nil; m=next){
398
			next = m->next;
399
			if(m->w){
400
				if(windel(m->w, 0))
401
					m->w = nil;
402
				else
403
					ok = 0;
404
			}
405
		}
406
		if(ok){
407
			windel(w, 1);
408
			removeupasfs();
409
			threadexitsall(nil);
410
		}
411
		return 1;
412
	}
413
	if(strcmp(s, "Put") == 0){
414
		rewritembox(wbox, &mbox);
415
		return 1;
416
	}
417
	if(strcmp(s, "Delmesg") == 0){
418
		if(nargs > 1){
419
			for(i=1; i<nargs; i++){
420
				snprint(buf, sizeof buf, "%s%s", mbox.name, args[i]);
421
				delmesg(buf, nil, 1);
422
			}
423
		}
424
		s = winselection(w);
425
		if(s == nil)
426
			return 1;
427
		nargs = 1;
428
		for(i=0; s[i]; i++)
429
			if(s[i] == '\n')
430
				nargs++;
431
		targs = emalloc(nargs*sizeof(char*));	/* could be too many for a local array */
432
		nargs = getfields(s, targs, nargs, 1, "\n");
433
		for(i=0; i<nargs; i++){
434
			if(!isdigit(targs[i][0]))
435
				continue;
436
			j = atoi(targs[i]);	/* easy way to parse the number! */
437
			if(j == 0)
438
				continue;
439
			snprint(buf, sizeof buf, "%s%d", mbox.name, j);
440
			delmesg(buf, nil, 1);
441
		}
442
		free(s);
443
		free(targs);
444
		return 1;
445
	}
446
	return 0;
447
}
448
 
449
void
450
mainctl(void *v)
451
{
452
	Window *w;
453
	Event *e, *e2, *eq, *ea;
454
	int na, nopen;
455
	char *s, *t, *buf;
456
 
457
	w = v;
458
	proccreate(wineventproc, w, STACK);
459
 
460
	for(;;){
461
		e = recvp(w->cevent);
462
		switch(e->c1){
463
		default:
464
		Unknown:
465
			print("unknown message %c%c\n", e->c1, e->c2);
466
			break;
467
 
468
		case 'E':	/* write to body; can't affect us */
469
			break;
470
 
471
		case 'F':	/* generated by our actions; ignore */
472
			break;
473
 
474
		case 'K':	/* type away; we don't care */
475
			break;
476
 
477
		case 'M':
478
			switch(e->c2){
479
			case 'x':
480
			case 'X':
481
				ea = nil;
482
				e2 = nil;
483
				if(e->flag & 2)
484
					e2 = recvp(w->cevent);
485
				if(e->flag & 8){
486
					ea = recvp(w->cevent);
487
					na = ea->nb;
488
					recvp(w->cevent);
489
				}else
490
					na = 0;
491
				s = e->b;
492
				/* if it's a known command, do it */
493
				if((e->flag&2) && e->nb==0)
494
					s = e2->b;
495
				if(na){
496
					t = emalloc(strlen(s)+1+na+1);
497
					sprint(t, "%s %s", s, ea->b);
498
					s = t;
499
				}
500
				/* if it's a long message, it can't be for us anyway */
501
				if(!mboxcommand(w, s))	/* send it back */
502
					winwriteevent(w, e);
503
				if(na)
504
					free(s);
505
				break;
506
 
507
			case 'l':
508
			case 'L':
509
				buf = nil;
510
				eq = e;
511
				if(e->flag & 2){
512
					e2 = recvp(w->cevent);
513
					eq = e2;
514
				}
515
				s = eq->b;
516
				if(eq->q1>eq->q0 && eq->nb==0){
517
					buf = emalloc((eq->q1-eq->q0)*UTFmax+1);
518
					winread(w, eq->q0, eq->q1, buf);
519
					s = buf;
520
				}
521
				nopen = 0;
522
				do{
523
					/* skip 'deleted' string if present' */
524
					if(strncmp(s, deleted, strlen(deleted)) == 0)
525
						s += strlen(deleted);
526
					/* skip mail box name if present */
527
					if(strncmp(s, mbox.name, strlen(mbox.name)) == 0)
528
						s += strlen(mbox.name);
529
					nopen += mesgopen(&mbox, mbox.name, s, nil, 0, nil);
530
					while(*s!='\0' && *s++!='\n')
531
						;
532
				}while(*s);
533
				if(nopen == 0)	/* send it back */
534
					winwriteevent(w, e);
535
				free(buf);
536
				break;
537
 
538
			case 'I':	/* modify away; we don't care */
539
			case 'D':
540
			case 'd':
541
			case 'i':
542
				break;
543
 
544
			default:
545
				goto Unknown;
546
			}
547
		}
548
	}
549
}
550