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 */
30 void plumbproc(void*);
31 void plumbshowproc(void*);
32 void plumbsendproc(void*);
33 void plumbthread(void);
34 void plumbshowthread(void*);
35 void plumbsendthread(void*);
42 fprint(2, "usage: Mail [-sS] [-o outgoing] [mailboxname [directoryname]]\n");
43 threadexitsall("usage");
51 if(strcmp(mboxname, "mbox") == 0)
53 snprint(buf, sizeof buf, "close %s", mboxname);
54 write(mbox.ctlfd, buf, strlen(buf));
64 snprint(buf, sizeof buf, "%s%s", maildir, s);
68 ret = d->qid.type & QTDIR;
74 threadmain(int argc, char *argv[])
77 char err[ERRMAX], *cmd;
81 doquote = needsrcquote;
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);
98 outgoing = EARGF(usage());
101 smprint(maildir, "%s/", EARGF(usage()));
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);
118 /* see if the name is that of an existing /mail/fs directory */
119 if(argc==1 && strchr(argv[0], '/')==0 && ismaildir(argv[0])){
121 mboxname = eappend(estrdup(maildir), "", name);
124 if(argv[0][i-1] == '/')
126 s = strrchr(argv[0], '/');
128 mboxname = estrdup(argv[0]);
133 mailboxdir = argv[0];
134 mboxname = estrdup(s);
143 user = getenv("user");
146 if(mailboxdir == nil)
147 mailboxdir = estrstrdup("/mail/box/", user);
149 outgoing = estrstrdup(mailboxdir, "/outgoing");
151 s = estrstrdup(maildir, "ctl");
152 mbox.ctlfd = open(s, ORDWR|OCEXEC);
154 error("can't open %s: %r", s);
156 fsname = estrdup(name);
157 if(newdir && argc > 0){
158 s = emalloc(5+strlen(mailboxdir)+strlen(mboxname)+strlen(name)+10+1);
160 sprint(s, "open %s/%s %s", mailboxdir, mboxname, fsname);
161 if(write(mbox.ctlfd, s, strlen(s)) >= 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);
168 fsname = emalloc(strlen(name)+10);
169 sprint(fsname, "%s-%d", name, i);
172 error("can't open %s/%s: %r", mailboxdir, mboxname);
176 s = estrstrdup(fsname, "/");
177 mbox.name = estrstrdup(maildir, s);
179 readmbox(&mbox, maildir, s);
180 home = getenv("home");
185 winname(wbox, mbox.name);
186 wintagwrite(wbox, "Put Mail Delmesg ", 3+1+4+1+7+1);
187 threadcreate(mainctl, wbox, STACK);
190 fmtprint(&fmt, "Mail");
192 fmtprint(&fmt, " -%c", "sS"[shortmenu-1]);
194 fmtprint(&fmt, " -o %s", outgoing);
195 fmtprint(&fmt, " %s", name);
196 cmd = fmtstrflush(&fmt);
198 sysfatal("out of memory");
199 winsetdump(wbox, "/acme/mail", cmd);
202 mesgmenu(wbox, &mbox);
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){
210 * Avoid creating multiple windows to send mail by only accepting
211 * sendmail plumb messages if we're reading the main mailbox.
213 plumbsendmailfd = plumbopen("sendmail", OREAD|OCEXEC);
214 cplumbsend = chancreate(sizeof(Plumbmsg*), 0);
215 proccreate(plumbsendproc, nil, STACK);
216 threadcreate(plumbsendthread, nil, STACK);
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 */
231 threadsetname("plumbproc");
233 m = plumbrecv(plumbseemailfd);
245 threadsetname("plumbshowproc");
247 m = plumbrecv(plumbshowmailfd);
248 sendp(cplumbshow, m);
259 threadsetname("plumbsendproc");
261 m = plumbrecv(plumbsendmailfd);
262 sendp(cplumbsend, m);
269 newmesg(char *name, char *digest)
273 if(strncmp(name, mbox.name, strlen(mbox.name)) != 0)
274 return; /* message is about another mailbox */
275 if(mesglookupfile(&mbox, name, digest) != nil)
280 if(mesgadd(&mbox, mbox.name, d, digest))
281 mesgmenunew(wbox, &mbox);
286 showmesg(char *name, char *digest)
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);
300 delmesg(char *name, char *digest, int dodel)
304 m = mesglookupfile(&mbox, name, digest);
306 mesgmenumarkdel(wbox, &mbox, m, 0);
319 threadsetname("plumbthread");
320 while((m = recvp(cplumb)) != nil){
322 digest = plumblookup(a, "digest");
323 type = plumblookup(a, "mailtype");
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);
331 fprint(2, "Mail: unknown plumb attribute %s\n", type);
338 plumbshowthread(void*)
342 threadsetname("plumbshowthread");
343 while((m = recvp(cplumbshow)) != nil){
344 showmesg(m->data, plumblookup(m->attr, "digest"));
351 plumbsendthread(void*)
355 threadsetname("plumbsendthread");
356 while((m = recvp(cplumbsend)) != nil){
357 mkreply(nil, "Mail", m->data, m->attr, nil);
364 mboxcommand(Window *w, char *s)
366 char *args[10], **targs;
371 nargs = tokenize(s, args, nelem(args));
374 if(strcmp(args[0], "Mail") == 0){
376 mkreply(nil, "Mail", "", nil, nil);
378 mkreply(nil, "Mail", args[1], nil, nil);
381 if(strcmp(s, "Del") == 0){
384 fprint(2, "mail: mailbox not written\n");
388 for(m=mbox.head; m!=nil; m=next){
397 for(m=replies.head; m!=nil; m=next){
413 if(strcmp(s, "Put") == 0){
414 rewritembox(wbox, &mbox);
417 if(strcmp(s, "Delmesg") == 0){
419 for(i=1; i<nargs; i++){
420 snprint(buf, sizeof buf, "%s%s", mbox.name, args[i]);
421 delmesg(buf, nil, 1);
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]))
436 j = atoi(targs[i]); /* easy way to parse the number! */
439 snprint(buf, sizeof buf, "%s%d", mbox.name, j);
440 delmesg(buf, nil, 1);
453 Event *e, *e2, *eq, *ea;
458 proccreate(wineventproc, w, STACK);
461 e = recvp(w->cevent);
465 print("unknown message %c%c\n", e->c1, e->c2);
468 case 'E': /* write to body; can't affect us */
471 case 'F': /* generated by our actions; ignore */
474 case 'K': /* type away; we don't care */
484 e2 = recvp(w->cevent);
486 ea = recvp(w->cevent);
492 /* if it's a known command, do it */
493 if((e->flag&2) && e->nb==0)
496 t = emalloc(strlen(s)+1+na+1);
497 sprint(t, "%s %s", s, ea->b);
500 /* if it's a long message, it can't be for us anyway */
501 if(!mboxcommand(w, s)) /* send it back */
512 e2 = recvp(w->cevent);
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);
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')
533 if(nopen == 0) /* send it back */
538 case 'I': /* modify away; we don't care */