10 char *maildir = "Mail/"; /* mountpoint of mail file system */
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 */
24 CFid *plumbshowmailfd;
25 CFid *plumbsendmailfd;
31 void plumbproc(void*);
32 void plumbshowproc(void*);
33 void plumbsendproc(void*);
34 void plumbthread(void);
35 void plumbshowthread(void*);
36 void plumbsendthread(void*);
46 fprint(2, "usage: Mail [-sS] [-n srvname] [-o outgoing] [mailboxname [directoryname]]\n");
47 threadexitsall("usage");
55 if(strcmp(mboxname, "mbox") == 0)
57 snprint(buf, sizeof buf, "close %s", mboxname);
58 fswrite(mbox.ctlfd, buf, strlen(buf));
67 d = fsdirstat(mailfs, s);
70 ret = d->qid.type & QTDIR;
76 threadmain(int argc, char *argv[])
79 char err[ERRMAX], *cmd;
83 doquote = needsrcquote;
86 /* open these early so we won't miss notification of new mail messages while we read mbox */
87 if((plumbsendfd = plumbopenfid("send", OWRITE|OCEXEC)) == nil)
88 fprint(2, "warning: open plumb/send: %r\n");
89 if((plumbseemailfd = plumbopenfid("seemail", OREAD|OCEXEC)) == nil)
90 fprint(2, "warning: open plumb/seemail: %r\n");
91 if((plumbshowmailfd = plumbopenfid("showmail", OREAD|OCEXEC)) == nil)
92 fprint(2, "warning: open plumb/showmail: %r\n");
104 outgoing = EARGF(usage());
107 smprint(maildir, "%s/", EARGF(usage()));
110 srvname = EARGF(usage());
116 acmefs = nsmount("acme",nil);
118 error("cannot mount acme: %r");
119 mailfs = nsmount(srvname, nil);
121 error("cannot mount %s: %r", srvname);
130 /* see if the name is that of an existing /mail/fs directory */
131 if(argc==1 && argv[0][0] != '/' && ismaildir(argv[0])){
133 mboxname = estrdup(name);
136 if(argv[0][i-1] == '/')
138 s = strrchr(argv[0], '/');
140 mboxname = estrdup(argv[0]);
145 mailboxdir = argv[0];
146 mboxname = estrdup(s);
155 user = getenv("user");
158 home = getenv("home");
160 home = getenv("HOME");
162 error("can't find $home");
163 if(mailboxdir == nil)
164 mailboxdir = estrstrdup(home, "/mail");
166 outgoing = estrstrdup(mailboxdir, "/outgoing");
168 mbox.ctlfd = fsopen(mailfs, estrstrdup(mboxname, "/ctl"), OWRITE);
169 if(mbox.ctlfd == nil)
170 error("can't open %s: %r", estrstrdup(mboxname, "/ctl"));
172 fsname = estrdup(name);
173 if(newdir && argc > 0){
174 s = emalloc(5+strlen(mailboxdir)+strlen(mboxname)+strlen(name)+10+1);
176 sprint(s, "open %s/%s %s", mailboxdir, mboxname, fsname);
177 if(fswrite(mbox.ctlfd, s, strlen(s)) >= 0)
180 errstr(err, sizeof err);
181 if(strstr(err, "mbox name in use") == nil)
182 error("can't create directory %s for mail: %s", name, err);
184 fsname = emalloc(strlen(name)+10);
185 sprint(fsname, "%s-%d", name, i);
188 error("can't open %s/%s: %r", mailboxdir, mboxname);
192 s = estrstrdup(fsname, "/");
193 mbox.name = estrstrdup(maildir, s);
195 readmbox(&mbox, maildir, s);
196 home = getenv("home");
201 winname(wbox, mbox.name);
202 wintagwrite(wbox, "Put Mail Delmesg ", 3+1+4+1+7+1);
203 threadcreate(mainctl, wbox, STACK);
206 fmtprint(&fmt, "Mail");
208 fmtprint(&fmt, " -%c", "sS"[shortmenu-1]);
210 fmtprint(&fmt, " -o %s", outgoing);
211 fmtprint(&fmt, " %s", name);
212 cmd = fmtstrflush(&fmt);
214 sysfatal("out of memory");
215 winsetdump(wbox, "/acme/mail", cmd);
218 mesgmenu(wbox, &mbox);
221 /* wctlfd = open("/dev/wctl", OWRITE|OCEXEC); /* for acme window */
223 cplumb = chancreate(sizeof(Plumbmsg*), 0);
224 cplumbshow = chancreate(sizeof(Plumbmsg*), 0);
225 if(strcmp(name, "mbox") == 0){
227 * Avoid creating multiple windows to send mail by only accepting
228 * sendmail plumb messages if we're reading the main mailbox.
230 plumbsendmailfd = plumbopenfid("sendmail", OREAD|OCEXEC);
231 cplumbsend = chancreate(sizeof(Plumbmsg*), 0);
232 proccreate(plumbsendproc, nil, STACK);
233 threadcreate(plumbsendthread, nil, STACK);
235 /* start plumb reader as separate proc ... */
236 proccreate(plumbproc, nil, STACK);
237 proccreate(plumbshowproc, nil, STACK);
238 threadcreate(plumbshowthread, nil, STACK);
239 fswrite(mbox.ctlfd, "refresh", 7);
240 /* ... and use this thread to read the messages */
249 threadsetname("plumbproc");
251 m = plumbrecvfid(plumbseemailfd);
259 plumbshowproc(void* v)
263 threadsetname("plumbshowproc");
265 m = plumbrecvfid(plumbshowmailfd);
266 sendp(cplumbshow, m);
273 plumbsendproc(void* v)
277 threadsetname("plumbsendproc");
279 m = plumbrecvfid(plumbsendmailfd);
280 sendp(cplumbsend, m);
287 newmesg(char *name, char *digest)
291 if(strncmp(name, mbox.name, strlen(mbox.name)) != 0)
292 return; /* message is about another mailbox */
293 if(mesglookupfile(&mbox, name, digest) != nil)
295 if(strncmp(name, "Mail/", 5) == 0)
297 d = fsdirstat(mailfs, name);
300 if(mesgadd(&mbox, mbox.name, d, digest))
301 mesgmenunew(wbox, &mbox);
306 showmesg(char *name, char *digest)
312 if(strncmp(name, mb, strlen(mb)) != 0)
313 return; /* message is about another mailbox */
314 n = estrdup(name+strlen(mb));
315 if(n[strlen(n)-1] != '/')
316 n = egrow(n, "/", nil);
317 mesgopen(&mbox, mbox.name, name+strlen(mb), nil, 1, digest);
322 delmesg(char *name, char *digest, int dodel, char *save)
326 m = mesglookupfile(&mbox, name, digest);
329 mesgcommand(m, estrstrdup("Save ", save));
331 mesgmenumarkdel(wbox, &mbox, m, 1);
333 /* notification came from plumber - message is gone */
334 mesgmenudel(wbox, &mbox, m);
348 threadsetname("plumbthread");
349 while((m = recvp(cplumb)) != nil){
351 digest = plumblookup(a, "digest");
352 type = plumblookup(a, "mailtype");
354 fprint(2, "Mail: plumb message with no mailtype attribute\n");
355 else if(strcmp(type, "new") == 0)
356 newmesg(m->data, digest);
357 else if(strcmp(type, "delete") == 0)
358 delmesg(m->data, digest, 0, nil);
360 fprint(2, "Mail: unknown plumb attribute %s\n", type);
367 plumbshowthread(void *v)
372 threadsetname("plumbshowthread");
373 while((m = recvp(cplumbshow)) != nil){
374 showmesg(m->data, plumblookup(m->attr, "digest"));
381 plumbsendthread(void *v)
386 threadsetname("plumbsendthread");
387 while((m = recvp(cplumbsend)) != nil){
388 mkreply(nil, "Mail", m->data, m->attr, nil);
395 mboxcommand(Window *w, char *s)
397 char *args[10], **targs, *save;
402 nargs = tokenize(s, args, nelem(args));
405 if(strcmp(args[0], "Mail") == 0){
407 mkreply(nil, "Mail", "", nil, nil);
409 mkreply(nil, "Mail", args[1], nil, nil);
412 if(strcmp(s, "Del") == 0){
415 fprint(2, "mail: mailbox not written\n");
419 for(m=mbox.head; m!=nil; m=next){
428 for(m=replies.head; m!=nil; m=next){
444 if(strcmp(s, "Put") == 0){
445 rewritembox(wbox, &mbox);
448 if(strcmp(s, "Get") == 0){
449 fswrite(mbox.ctlfd, "refresh", 7);
452 if(strcmp(s, "Delmesg") == 0){
463 targs = emalloc(nargs*sizeof(char*)); /* could be too many for a local array */
464 nargs = getfields(s, targs, nargs, 1, "\n");
465 for(i=0; i<nargs; i++){
466 if(!isdigit(targs[i][0]))
468 j = atoi(targs[i]); /* easy way to parse the number! */
471 snprint(buf, sizeof buf, "%s%d", mbox.name, j);
472 delmesg(buf, nil, 1, save);
485 Event *e, *e2, *eq, *ea;
491 proccreate(wineventproc, w, STACK);
494 e = recvp(w->cevent);
498 print("unknown message %c%c\n", e->c1, e->c2);
501 case 'E': /* write to body; can't affect us */
504 case 'F': /* generated by our actions; ignore */
507 case 'K': /* type away; we don't care */
517 e2 = recvp(w->cevent);
519 ea = recvp(w->cevent);
525 /* if it's a known command, do it */
526 if((e->flag&2) && e->nb==0)
529 t = emalloc(strlen(s)+1+na+1);
530 sprint(t, "%s %s", s, ea->b);
533 /* if it's a long message, it can't be for us anyway */
534 if(!mboxcommand(w, s)) /* send it back */
545 e2 = recvp(w->cevent);
549 if(eq->q1>eq->q0 && eq->nb==0){
550 buf = emalloc((eq->q1-eq->q0)*UTFmax+1);
551 winread(w, eq->q0, eq->q1, buf);
556 /* skip 'deleted' string if present' */
557 if(strncmp(s, deleted, strlen(deleted)) == 0)
558 s += strlen(deleted);
559 /* skip mail box name if present */
560 if(strncmp(s, mbox.name, strlen(mbox.name)) == 0)
561 s += strlen(mbox.name);
562 nopen += mesgopen(&mbox, mbox.name, s, nil, 0, nil);
563 while(*s!='\0' && *s++!='\n')
566 if(nopen == 0) /* send it back */
571 case 'I': /* modify away; we don't care */