URI:
       tmail.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tmail.c (13406B)
       ---
            1 #include <u.h>
            2 #include <libc.h>
            3 #include <bio.h>
            4 #include <thread.h>
            5 #include <9pclient.h>
            6 #include <plumb.h>
            7 #include <ctype.h>
            8 #include "dat.h"
            9 
           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 */
           14 char        *user;
           15 char        *outgoing;
           16 char *srvname;
           17 
           18 Window        *wbox;
           19 Message        mbox;
           20 Message        replies;
           21 char                *home;
           22 CFid                *plumbsendfd;
           23 CFid                *plumbseemailfd;
           24 CFid                *plumbshowmailfd;
           25 CFid                *plumbsendmailfd;
           26 Channel        *cplumb;
           27 Channel        *cplumbshow;
           28 Channel        *cplumbsend;
           29 int                wctlfd;
           30 void                mainctl(void*);
           31 void                plumbproc(void*);
           32 void                plumbshowproc(void*);
           33 void                plumbsendproc(void*);
           34 void                plumbthread(void);
           35 void                plumbshowthread(void*);
           36 void                plumbsendthread(void*);
           37 
           38 int                        shortmenu;
           39 
           40 CFsys *mailfs;
           41 CFsys *acmefs;
           42 
           43 void
           44 usage(void)
           45 {
           46         fprint(2, "usage: Mail [-sS] [-n srvname] [-o outgoing] [mailboxname [directoryname]]\n");
           47         threadexitsall("usage");
           48 }
           49 
           50 void
           51 removeupasfs(void)
           52 {
           53         char buf[256];
           54 
           55         if(strcmp(mboxname, "mbox") == 0)
           56                 return;
           57         snprint(buf, sizeof buf, "close %s", mboxname);
           58         fswrite(mbox.ctlfd, buf, strlen(buf));
           59 }
           60 
           61 int
           62 ismaildir(char *s)
           63 {
           64         Dir *d;
           65         int ret;
           66 
           67         d = fsdirstat(mailfs, s);
           68         if(d == nil)
           69                 return 0;
           70         ret = d->qid.type & QTDIR;
           71         free(d);
           72         return ret;
           73 }
           74 
           75 void
           76 threadmain(int argc, char *argv[])
           77 {
           78         char *s, *name;
           79         char err[ERRMAX], *cmd;
           80         int i, newdir;
           81         Fmt fmt;
           82 
           83         doquote = needsrcquote;
           84         quotefmtinstall();
           85 
           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");
           93 
           94         shortmenu = 0;
           95         srvname = "mail";
           96         ARGBEGIN{
           97         case 's':
           98                 shortmenu = 1;
           99                 break;
          100         case 'S':
          101                 shortmenu = 2;
          102                 break;
          103         case 'o':
          104                 outgoing = EARGF(usage());
          105                 break;
          106         case 'm':
          107                 smprint(maildir, "%s/", EARGF(usage()));
          108                 break;
          109         case 'n':
          110                 srvname = EARGF(usage());
          111                 break;
          112         default:
          113                 usage();
          114         }ARGEND
          115 
          116         acmefs = nsmount("acme",nil);
          117         if(acmefs == nil)
          118                 error("cannot mount acme: %r");
          119         mailfs = nsmount(srvname, nil);
          120         if(mailfs == nil)
          121                 error("cannot mount %s: %r", srvname);
          122 
          123         name = "mbox";
          124 
          125         newdir = 1;
          126         if(argc > 0){
          127                 i = strlen(argv[0]);
          128                 if(argc>2 || i==0)
          129                         usage();
          130                 /* see if the name is that of an existing /mail/fs directory */
          131                 if(argc==1 && argv[0][0] != '/' && ismaildir(argv[0])){
          132                         name = argv[0];
          133                         mboxname = estrdup(name);
          134                         newdir = 0;
          135                 }else{
          136                         if(argv[0][i-1] == '/')
          137                                 argv[0][i-1] = '\0';
          138                         s = strrchr(argv[0], '/');
          139                         if(s == nil)
          140                                 mboxname = estrdup(argv[0]);
          141                         else{
          142                                 *s++ = '\0';
          143                                 if(*s == '\0')
          144                                         usage();
          145                                 mailboxdir = argv[0];
          146                                 mboxname = estrdup(s);
          147                         }
          148                         if(argc > 1)
          149                                 name = argv[1];
          150                         else
          151                                 name = mboxname;
          152                 }
          153         }
          154 
          155         user = getenv("user");
          156         if(user == nil)
          157                 user = "none";
          158         home = getenv("home");
          159         if(home == nil)
          160                 home = getenv("HOME");
          161         if(home == nil)
          162                 error("can't find $home");
          163         if(mailboxdir == nil)
          164                 mailboxdir = estrstrdup(home, "/mail");
          165         if(outgoing == nil)
          166                 outgoing = estrstrdup(mailboxdir, "/outgoing");
          167 
          168         mbox.ctlfd = fsopen(mailfs, estrstrdup(mboxname, "/ctl"), OWRITE);
          169         if(mbox.ctlfd == nil)
          170                 error("can't open %s: %r", estrstrdup(mboxname, "/ctl"));
          171 
          172         fsname = estrdup(name);
          173         if(newdir && argc > 0){
          174                 s = emalloc(5+strlen(mailboxdir)+strlen(mboxname)+strlen(name)+10+1);
          175                 for(i=0; i<10; i++){
          176                         sprint(s, "open %s/%s %s", mailboxdir, mboxname, fsname);
          177                         if(fswrite(mbox.ctlfd, s, strlen(s)) >= 0)
          178                                 break;
          179                         err[0] = '\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);
          183                         free(fsname);
          184                         fsname = emalloc(strlen(name)+10);
          185                         sprint(fsname, "%s-%d", name, i);
          186                 }
          187                 if(i == 10)
          188                         error("can't open %s/%s: %r", mailboxdir, mboxname);
          189                 free(s);
          190         }
          191 
          192         s = estrstrdup(fsname, "/");
          193         mbox.name = estrstrdup(maildir, s);
          194         mbox.level= 0;
          195         readmbox(&mbox, maildir, s);
          196         home = getenv("home");
          197         if(home == nil)
          198                 home = "/";
          199 
          200         wbox = newwindow();
          201         winname(wbox, mbox.name);
          202         wintagwrite(wbox, "Put Mail Delmesg ", 3+1+4+1+7+1);
          203         threadcreate(mainctl, wbox, STACK);
          204 
          205         fmtstrinit(&fmt);
          206         fmtprint(&fmt, "Mail");
          207         if(shortmenu)
          208                 fmtprint(&fmt, " -%c", "sS"[shortmenu-1]);
          209         if(outgoing)
          210                 fmtprint(&fmt, " -o %s", outgoing);
          211         fmtprint(&fmt, " %s", name);
          212         cmd = fmtstrflush(&fmt);
          213         if(cmd == nil)
          214                 sysfatal("out of memory");
          215         winsetdump(wbox, "/acme/mail", cmd);
          216         mbox.w = wbox;
          217 
          218         mesgmenu(wbox, &mbox);
          219         winclean(wbox);
          220 
          221 /*        wctlfd = open("/dev/wctl", OWRITE|OCEXEC);        /* for acme window */
          222         wctlfd = -1;
          223         cplumb = chancreate(sizeof(Plumbmsg*), 0);
          224         cplumbshow = chancreate(sizeof(Plumbmsg*), 0);
          225         if(strcmp(name, "mbox") == 0){
          226                 /*
          227                  * Avoid creating multiple windows to send mail by only accepting
          228                  * sendmail plumb messages if we're reading the main mailbox.
          229                  */
          230                 plumbsendmailfd = plumbopenfid("sendmail", OREAD|OCEXEC);
          231                 cplumbsend = chancreate(sizeof(Plumbmsg*), 0);
          232                 proccreate(plumbsendproc, nil, STACK);
          233                 threadcreate(plumbsendthread, nil, STACK);
          234         }
          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 */
          241         plumbthread();
          242 }
          243 
          244 void
          245 plumbproc(void* v)
          246 {
          247         Plumbmsg *m;
          248 
          249         threadsetname("plumbproc");
          250         for(;;){
          251                 m = plumbrecvfid(plumbseemailfd);
          252                 sendp(cplumb, m);
          253                 if(m == nil)
          254                         threadexits(nil);
          255         }
          256 }
          257 
          258 void
          259 plumbshowproc(void* v)
          260 {
          261         Plumbmsg *m;
          262 
          263         threadsetname("plumbshowproc");
          264         for(;;){
          265                 m = plumbrecvfid(plumbshowmailfd);
          266                 sendp(cplumbshow, m);
          267                 if(m == nil)
          268                         threadexits(nil);
          269         }
          270 }
          271 
          272 void
          273 plumbsendproc(void* v)
          274 {
          275         Plumbmsg *m;
          276 
          277         threadsetname("plumbsendproc");
          278         for(;;){
          279                 m = plumbrecvfid(plumbsendmailfd);
          280                 sendp(cplumbsend, m);
          281                 if(m == nil)
          282                         threadexits(nil);
          283         }
          284 }
          285 
          286 void
          287 newmesg(char *name, char *digest)
          288 {
          289         Dir *d;
          290 
          291         if(strncmp(name, mbox.name, strlen(mbox.name)) != 0)
          292                 return;        /* message is about another mailbox */
          293         if(mesglookupfile(&mbox, name, digest) != nil)
          294                 return;
          295         if(strncmp(name, "Mail/", 5) == 0)
          296                 name += 5;
          297         d = fsdirstat(mailfs, name);
          298         if(d == nil)
          299                 return;
          300         if(mesgadd(&mbox, mbox.name, d, digest))
          301                 mesgmenunew(wbox, &mbox);
          302         free(d);
          303 }
          304 
          305 void
          306 showmesg(char *name, char *digest)
          307 {
          308         char *n;
          309         char *mb;
          310 
          311         mb = mbox.name;
          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);
          318         free(n);
          319 }
          320 
          321 void
          322 delmesg(char *name, char *digest, int dodel, char *save)
          323 {
          324         Message *m;
          325 
          326         m = mesglookupfile(&mbox, name, digest);
          327         if(m != nil){
          328                 if(save)
          329                         mesgcommand(m, estrstrdup("Save ", save));
          330                 if(dodel)
          331                         mesgmenumarkdel(wbox, &mbox, m, 1);
          332                 else{
          333                         /* notification came from plumber - message is gone */
          334                         mesgmenudel(wbox, &mbox, m);
          335                         if(!m->opened)
          336                                 mesgdel(&mbox, m);
          337                 }
          338         }
          339 }
          340 
          341 void
          342 plumbthread(void)
          343 {
          344         Plumbmsg *m;
          345         Plumbattr *a;
          346         char *type, *digest;
          347 
          348         threadsetname("plumbthread");
          349         while((m = recvp(cplumb)) != nil){
          350                 a = m->attr;
          351                 digest = plumblookup(a, "digest");
          352                 type = plumblookup(a, "mailtype");
          353                 if(type == nil)
          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);
          359                 else
          360                         fprint(2, "Mail: unknown plumb attribute %s\n", type);
          361                 plumbfree(m);
          362         }
          363         threadexits(nil);
          364 }
          365 
          366 void
          367 plumbshowthread(void *v)
          368 {
          369         Plumbmsg *m;
          370 
          371         USED(v);
          372         threadsetname("plumbshowthread");
          373         while((m = recvp(cplumbshow)) != nil){
          374                 showmesg(m->data, plumblookup(m->attr, "digest"));
          375                 plumbfree(m);
          376         }
          377         threadexits(nil);
          378 }
          379 
          380 void
          381 plumbsendthread(void *v)
          382 {
          383         Plumbmsg *m;
          384 
          385         USED(v);
          386         threadsetname("plumbsendthread");
          387         while((m = recvp(cplumbsend)) != nil){
          388                 mkreply(nil, "Mail", m->data, m->attr, nil);
          389                 plumbfree(m);
          390         }
          391         threadexits(nil);
          392 }
          393 
          394 int
          395 mboxcommand(Window *w, char *s)
          396 {
          397         char *args[10], **targs, *save;
          398         Window *sbox;
          399         Message *m, *next;
          400         int ok, nargs, i, j;
          401         CFid *searchfd;
          402         char buf[128], *res;
          403 
          404         nargs = tokenize(s, args, nelem(args));
          405         if(nargs == 0)
          406                 return 0;
          407         if(strcmp(args[0], "Mail") == 0){
          408                 if(nargs == 1)
          409                         mkreply(nil, "Mail", "", nil, nil);
          410                 else
          411                         mkreply(nil, "Mail", args[1], nil, nil);
          412                 return 1;
          413         }
          414         if(strcmp(s, "Del") == 0){
          415                 if(mbox.dirty){
          416                         mbox.dirty = 0;
          417                         fprint(2, "mail: mailbox not written\n");
          418                         return 1;
          419                 }
          420                 if(w != mbox.w){
          421                         windel(w, 1);
          422                         return 1;
          423                 }
          424                 ok = 1;
          425                 for(m=mbox.head; m!=nil; m=next){
          426                         next = m->next;
          427                         if(m->w){
          428                                 if(windel(m->w, 0))
          429                                         m->w = nil;
          430                                 else
          431                                         ok = 0;
          432                         }
          433                 }
          434                 for(m=replies.head; m!=nil; m=next){
          435                         next = m->next;
          436                         if(m->w){
          437                                 if(windel(m->w, 0))
          438                                         m->w = nil;
          439                                 else
          440                                         ok = 0;
          441                         }
          442                 }
          443                 if(ok){
          444                         windel(w, 1);
          445                         removeupasfs();
          446                         threadexitsall(nil);
          447                 }
          448                 return 1;
          449         }
          450         if(strcmp(s, "Put") == 0){
          451                 rewritembox(wbox, &mbox);
          452                 return 1;
          453         }
          454         if(strcmp(s, "Get") == 0){
          455                 fswrite(mbox.ctlfd, "refresh", 7);
          456                 return 1;
          457         }
          458         if(strcmp(s, "Delmesg") == 0){
          459                 save = nil;
          460                 if(nargs > 1)
          461                         save = args[1];
          462                 s = winselection(w);
          463                 if(s == nil)
          464                         return 1;
          465                 nargs = 1;
          466                 for(i=0; s[i]; i++)
          467                         if(s[i] == '\n')
          468                                 nargs++;
          469                 targs = emalloc(nargs*sizeof(char*));        /* could be too many for a local array */
          470                 nargs = getfields(s, targs, nargs, 1, "\n");
          471                 for(i=0; i<nargs; i++){
          472                         if(!isdigit(targs[i][0]))
          473                                 continue;
          474                         j = atoi(targs[i]);        /* easy way to parse the number! */
          475                         if(j == 0)
          476                                 continue;
          477                         snprint(buf, sizeof buf, "%s%d", mbox.name, j);
          478                         delmesg(buf, nil, 1, save);
          479                 }
          480                 free(s);
          481                 free(targs);
          482                 return 1;
          483         }
          484         if(strcmp(s, "Search") == 0){
          485                 if(nargs <= 1)
          486                         return 1;
          487                 s = estrstrdup(mboxname, "/search");
          488                 searchfd = fsopen(mailfs, s, ORDWR);
          489                 if(searchfd == nil)
          490                         return 1;
          491                 save = estrdup(args[1]);
          492                 for(i=2; i<nargs; i++)
          493                         save = eappend(save, " ", args[i]);
          494                 fswrite(searchfd, save, strlen(save));
          495                 fsseek(searchfd, 0, 0);
          496                 j = fsread(searchfd, buf, sizeof buf - 1);
          497                  if(j == 0){
          498                         fprint(2, "[%s] search %s: no results found\n", mboxname, save);
          499                         fsclose(searchfd);
          500                         free(save);
          501                         return 1;
          502                 }
          503                 free(save);
          504                 buf[j] = '\0';
          505                 res = estrdup(buf);
          506                 j = fsread(searchfd, buf, sizeof buf - 1);
          507                 for(; j != 0; j = fsread(searchfd, buf, sizeof buf - 1), buf[j] = '\0')
          508                         res = eappend(res, "", buf);
          509                 fsclose(searchfd);
          510 
          511                 sbox = newwindow();
          512                 winname(sbox, s);
          513                 free(s);
          514                 threadcreate(mainctl, sbox, STACK);
          515                 winopenbody(sbox, OWRITE);
          516 
          517                 /* show results in reverse order */
          518                 m = mbox.tail;
          519                 save = nil;
          520                 for(s=strrchr(res, ' '); s!=nil || save!=res; s=strrchr(res, ' ')){
          521                         if(s != nil){
          522                                 save = s+1;
          523                                 *s = '\0';
          524                         }
          525                         else save = res;
          526                         save = estrstrdup(save, "/");
          527                         for(; m && strcmp(save, m->name) != 0; m=m->prev);
          528                         free(save);
          529                         if(m == nil)
          530                                 break;
          531                         fsprint(sbox->body, "%s%s\n", m->name, info(m, 0, 0));
          532                         m = m->prev;
          533                 }
          534                 free(res);
          535                 winclean(sbox);
          536                 winclosebody(sbox);
          537                 return 1;
          538         }
          539         return 0;
          540 }
          541 
          542 void
          543 mainctl(void *v)
          544 {
          545         Window *w;
          546         Event *e, *e2, *eq, *ea;
          547         int na, nopen;
          548         char *s, *t, *buf;
          549 
          550         w = v;
          551         winincref(w);
          552         proccreate(wineventproc, w, STACK);
          553 
          554         for(;;){
          555                 e = recvp(w->cevent);
          556                 switch(e->c1){
          557                 default:
          558                 Unknown:
          559                         print("unknown message %c%c\n", e->c1, e->c2);
          560                         break;
          561 
          562                 case 'E':        /* write to body; can't affect us */
          563                         break;
          564 
          565                 case 'F':        /* generated by our actions; ignore */
          566                         break;
          567 
          568                 case 'K':        /* type away; we don't care */
          569                         break;
          570 
          571                 case 'M':
          572                         switch(e->c2){
          573                         case 'x':
          574                         case 'X':
          575                                 ea = nil;
          576                                 e2 = nil;
          577                                 if(e->flag & 2)
          578                                         e2 = recvp(w->cevent);
          579                                 if(e->flag & 8){
          580                                         ea = recvp(w->cevent);
          581                                         na = ea->nb;
          582                                         recvp(w->cevent);
          583                                 }else
          584                                         na = 0;
          585                                 s = e->b;
          586                                 /* if it's a known command, do it */
          587                                 if((e->flag&2) && e->nb==0)
          588                                         s = e2->b;
          589                                 if(na){
          590                                         t = emalloc(strlen(s)+1+na+1);
          591                                         sprint(t, "%s %s", s, ea->b);
          592                                         s = t;
          593                                 }
          594                                 /* if it's a long message, it can't be for us anyway */
          595                                 if(!mboxcommand(w, s))        /* send it back */
          596                                         winwriteevent(w, e);
          597                                 if(na)
          598                                         free(s);
          599                                 break;
          600 
          601                         case 'l':
          602                         case 'L':
          603                                 buf = nil;
          604                                 eq = e;
          605                                 if(e->flag & 2){
          606                                         e2 = recvp(w->cevent);
          607                                         eq = e2;
          608                                 }
          609                                 s = eq->b;
          610                                 if(eq->q1>eq->q0 && eq->nb==0){
          611                                         buf = emalloc((eq->q1-eq->q0)*UTFmax+1);
          612                                         winread(w, eq->q0, eq->q1, buf);
          613                                         s = buf;
          614                                 }
          615                                 nopen = 0;
          616                                 do{
          617                                         /* skip 'deleted' string if present' */
          618                                         if(strncmp(s, deleted, strlen(deleted)) == 0)
          619                                                 s += strlen(deleted);
          620                                         /* skip mail box name if present */
          621                                         if(strncmp(s, mbox.name, strlen(mbox.name)) == 0)
          622                                                 s += strlen(mbox.name);
          623                                         nopen += mesgopen(&mbox, mbox.name, s, nil, 0, nil);
          624                                         while(*s!='\0' && *s++!='\n')
          625                                                 ;
          626                                 }while(*s);
          627                                 if(nopen == 0)        /* send it back */
          628                                         winwriteevent(w, e);
          629                                 free(buf);
          630                                 break;
          631 
          632                         case 'I':        /* modify away; we don't care */
          633                         case 'D':
          634                         case 'd':
          635                         case 'i':
          636                                 break;
          637 
          638                         default:
          639                                 goto Unknown;
          640                         }
          641                 }
          642         }
          643 }