7 #include <9pclient.h> /* jpc */
10 char *maildir = "/mail/fs/"; /* mountpoint of mail file system */
11 char *mailtermdir = "/mnt/term/mail/fs/"; /* alternate mountpoint */
12 char *mboxname = "mbox"; /* mailboxdir/mboxname is mail spool file */
13 char *mailboxdir = nil; /* nil == /mail/box/$user */
14 char *fsname; /* filesystem for mailboxdir/mboxname is at maildir/fsname */
31 void plumbproc(void*);
32 void plumbshowproc(void*);
33 void plumbsendproc(void*);
34 void plumbthread(void);
35 void plumbshowthread(void*);
36 void plumbsendthread(void*);
40 CFsys *upasfs; /*jpc */
41 CFsys *acmefs; /*jpc */
46 fprint(2, "usage: Mail [-sS] [-o outgoing] [mailboxname [directoryname]]\n");
47 threadexitsall("usage");
55 if(strcmp(mboxname, "mbox") == 0)
57 snprint(buf, sizeof buf, "close %s", mboxname);
58 write(mbox.ctlfd, buf, strlen(buf));
68 snprint(buf, sizeof buf, "%s%s", maildir, s);
72 ret = d->qid.type & QTDIR;
78 threadmain(int argc, char *argv[])
81 char err[ERRMAX], *cmd;
85 doquote = needsrcquote;
88 /* open these early so we won't miss notification of new mail messages while we read mbox */
89 plumbsendfd = plumbopen("send", OWRITE|OCEXEC);
90 plumbseemailfd = plumbopen("seemail", OREAD|OCEXEC);
91 plumbshowmailfd = plumbopen("showmail", OREAD|OCEXEC);
94 acmefs = nsmount("acme",nil);
95 upasfs = nsmount("upasfs", nil);
107 outgoing = EARGF(usage());
110 smprint(maildir, "%s/", EARGF(usage()));
118 /* bind the terminal /mail/fs directory over the local one */
119 if(access(maildir, 0)<0 && access(mailtermdir, 0)==0) {
120 /* jpc - bind(mailtermdir, maildir, MAFTER); */
121 fprint(2,"jpc: trying to bind(mailtermdir, maildir, MAFTER)\n");
129 /* see if the name is that of an existing /mail/fs directory */
130 if(argc==1 && strchr(argv[0], '/')==0 && ismaildir(argv[0])){
132 mboxname = eappend(estrdup(maildir), "", name);
135 if(argv[0][i-1] == '/')
137 s = strrchr(argv[0], '/');
139 mboxname = estrdup(argv[0]);
144 mailboxdir = argv[0];
145 mboxname = estrdup(s);
154 user = getenv("user");
157 if(mailboxdir == nil)
158 mailboxdir = estrstrdup(unsharp("#9/mail/box/"), user);
160 outgoing = estrstrdup(mailboxdir, "/outgoing");
162 // s = estrstrdup(maildir, "ctl");
163 mbox.ctlfd = fsopenfd(upasfs,"ctl", ORDWR|OCEXEC);
165 error("can't open %s: %r\n", s);
167 fsname = estrdup(name);
168 if(newdir && argc > 0){
169 s = emalloc(5+strlen(mailboxdir)+strlen(mboxname)+strlen(name)+10+1);
171 sprint(s, "open %s/%s %s", mailboxdir, mboxname, fsname);
172 if(write(mbox.ctlfd, s, strlen(s)) >= 0)
175 errstr(err, sizeof err);
176 if(strstr(err, "mbox name in use") == nil)
177 error("can't create directory %s for mail: %s\n", name, err);
179 fsname = emalloc(strlen(name)+10);
180 sprint(fsname, "%s-%d", name, i);
183 error("can't open %s/%s: %r", mailboxdir, mboxname);
187 s = estrstrdup(fsname, "/");
188 mbox.name = estrstrdup(maildir, s);
189 // mbox.name = "/mail/fs/mbox/";
191 readmbox(&mbox, maildir, s);
192 home = getenv("home");
197 winname(wbox, mbox.name);
198 wintagwrite(wbox, "Put Mail Delmesg ", 3+1+4+1+7+1);
199 threadcreate(mainctl, wbox, STACK);
202 fmtprint(&fmt, "Mail");
204 fmtprint(&fmt, " -%c", "sS"[shortmenu-1]);
206 fmtprint(&fmt, " -o %s", outgoing);
207 fmtprint(&fmt, " %s", name);
208 cmd = fmtstrflush(&fmt);
210 sysfatal("out of memory");
211 winsetdump(wbox, "/acme/mail", cmd);
214 mesgmenu(wbox, &mbox);
218 wctlfd = open("/dev/wctl", OWRITE|OCEXEC); /* for acme window */
219 cplumb = chancreate(sizeof(Plumbmsg*), 0);
220 cplumbshow = chancreate(sizeof(Plumbmsg*), 0);
221 if(strcmp(name, "mbox") == 0){
223 * Avoid creating multiple windows to send mail by only accepting
224 * sendmail plumb messages if we're reading the main mailbox.
226 plumbsendmailfd = plumbopen("sendmail", OREAD|OCEXEC);
227 cplumbsend = chancreate(sizeof(Plumbmsg*), 0);
228 proccreate(plumbsendproc, nil, STACK);
229 threadcreate(plumbsendthread, nil, STACK);
231 /* start plumb reader as separate proc ... */
232 proccreate(plumbproc, nil, STACK);
233 proccreate(plumbshowproc, nil, STACK);
234 threadcreate(plumbshowthread, nil, STACK);
235 /* ... and use this thread to read the messages */
244 threadsetname("plumbproc");
246 m = plumbrecv(plumbseemailfd);
254 plumbshowproc(void* v)
258 threadsetname("plumbshowproc");
260 m = plumbrecv(plumbshowmailfd);
261 sendp(cplumbshow, m);
268 plumbsendproc(void* v)
272 threadsetname("plumbsendproc");
274 m = plumbrecv(plumbsendmailfd);
275 sendp(cplumbsend, m);
282 newmesg(char *name, char *digest)
287 if(strncmp(name, mbox.name, strlen(mbox.name)) != 0) {
288 return; /* message is about another mailbox */
290 if(mesglookupfile(&mbox, name, digest) != nil) {
293 if (strncmp(name,"/mail/fs/",strlen("/mail/fs/"))==0) {
294 tmp = name+strlen("/mail/fs/");
296 d = fsdirstat(upasfs,tmp);
300 if(mesgadd(&mbox, mbox.name, d, digest)) {
301 mesgmenunew(wbox, &mbox);
307 showmesg(char *name, char *digest)
311 if(strncmp(name, mbox.name, strlen(mbox.name)) != 0)
312 return; /* message is about another mailbox */
313 n = estrdup(name+strlen(mbox.name));
314 if(n[strlen(n)-1] != '/')
315 n = egrow(n, "/", nil);
316 mesgopen(&mbox, mbox.name, name+strlen(mbox.name), nil, 1, digest);
321 delmesg(char *name, char *digest, int dodel)
325 m = mesglookupfile(&mbox, name, digest);
327 mesgmenumarkdel(wbox, &mbox, m, 0);
340 threadsetname("plumbthread");
341 while((m = recvp(cplumb)) != nil){
343 digest = plumblookup(a, "digest");
344 type = plumblookup(a, "mailtype");
346 fprint(2, "Mail: plumb message with no mailtype attribute\n");
347 else if(strcmp(type, "new") == 0)
348 newmesg(m->data, digest);
349 else if(strcmp(type, "delete") == 0)
350 delmesg(m->data, digest, 0);
352 fprint(2, "Mail: unknown plumb attribute %s\n", type);
359 plumbshowthread(void* v)
363 threadsetname("plumbshowthread");
364 while((m = recvp(cplumbshow)) != nil){
365 showmesg(m->data, plumblookup(m->attr, "digest"));
372 plumbsendthread(void* v)
376 threadsetname("plumbsendthread");
377 while((m = recvp(cplumbsend)) != nil){
378 mkreply(nil, "Mail", m->data, m->attr, nil);
385 mboxcommand(Window *w, char *s)
387 char *args[10], **targs;
392 nargs = tokenize(s, args, nelem(args));
395 if(strcmp(args[0], "Mail") == 0){
397 mkreply(nil, "Mail", "", nil, nil);
399 mkreply(nil, "Mail", args[1], nil, nil);
402 if(strcmp(s, "Del") == 0){
405 fprint(2, "mail: mailbox not written\n");
409 for(m=mbox.head; m!=nil; m=next){
418 for(m=replies.head; m!=nil; m=next){
434 if(strcmp(s, "Put") == 0){
435 rewritembox(wbox, &mbox);
438 if(strcmp(s, "Delmesg") == 0){
440 for(i=1; i<nargs; i++){
441 snprint(buf, sizeof buf, "%s%s", mbox.name, args[i]);
442 delmesg(buf, nil, 1);
452 targs = emalloc(nargs*sizeof(char*)); /* could be too many for a local array */
453 nargs = getfields(s, targs, nargs, 1, "\n");
454 for(i=0; i<nargs; i++){
455 if(!isdigit(targs[i][0]))
457 j = atoi(targs[i]); /* easy way to parse the number! */
460 snprint(buf, sizeof buf, "%s%d", mbox.name, j);
461 delmesg(buf, nil, 1);
474 Event *e, *e2, *eq, *ea;
479 proccreate(wineventproc, w, STACK);
482 e = recvp(w->cevent);
486 print("unknown message %c%c\n", e->c1, e->c2);
489 case 'E': /* write to body; can't affect us */
492 case 'F': /* generated by our actions; ignore */
495 case 'K': /* type away; we don't care */
505 e2 = recvp(w->cevent);
507 ea = recvp(w->cevent);
513 /* if it's a known command, do it */
514 if((e->flag&2) && e->nb==0)
517 t = emalloc(strlen(s)+1+na+1);
518 sprint(t, "%s %s", s, ea->b);
521 /* if it's a long message, it can't be for us anyway */
522 if(!mboxcommand(w, s)) /* send it back */
533 e2 = recvp(w->cevent);
537 if(eq->q1>eq->q0 && eq->nb==0){
538 buf = emalloc((eq->q1-eq->q0)*UTFmax+1);
539 winread(w, eq->q0, eq->q1, buf);
544 /* skip 'deleted' string if present' */
545 if(strncmp(s, deleted, strlen(deleted)) == 0)
546 s += strlen(deleted);
547 /* skip mail box name if present */
548 if(strncmp(s, mbox.name, strlen(mbox.name)) == 0)
549 s += strlen(mbox.name);
550 nopen += mesgopen(&mbox, mbox.name, s, nil, 0, nil);
551 while(*s!='\0' && *s++!='\n')
554 if(nopen == 0) /* send it back */
559 case 'I': /* modify away; we don't care */