9 #define Datefmt "?WWW, ?MMM ?DD hh:mm:ss ?Z YYYY"
15 void (*fn)(Mesg *, char **, int);
23 for(i = 0; i < m->nparts; i++)
24 mesgclear(m->parts[i]);
51 line(char *data, char **pp, int z)
55 for(p=data; *p!='\0' && *p!='\n'; p++)
63 q = emalloc(p-data + 1);
64 memmove(q, data, p-data);
73 if(s != nil && strlen(m->from) != 0){
74 r = smprint("%s <%s>", s, m->from);
79 return estrdup(m->from);
93 m = emalloc(sizeof(Mesg));
94 m->name = estrjoin(name, "/", nil);
95 if((info = rslurp(m, "info", &ninfo)) == nil){
102 m->from = line(p, &p, 0);
103 m->to = line(p, &p, 0);
104 m->cc = line(p, &p, 0);
105 m->replyto = line(p, &p, 1);
106 m->date = line(p, &p, 0);
107 m->subject = line(p, &p, 0);
108 m->type = line(p, &p, 1);
109 m->disposition = line(p, &p, 1);
110 m->filename = line(p, &p, 1);
111 m->digest = line(p, &p, 1);
112 /* m->bcc = */ free(line(p, &p, 1));
113 m->inreplyto = line(p, &p, 1);
114 /* m->date = */ free(line(p, &p, 1));
115 /* m->sender = */ free(line(p, &p, 1));
116 m->messageid = line(p, &p, 0);
117 /* m->lines = */ free(line(p, &p, 1));
118 /* m->size = */ free(line(p, &p, 1));
119 m->mflags = line(p, &p, 0);
120 /* m->fileid = */ free(line(p, &p, 1));
121 m->fromcolon = fc(m, line(p, &p, 1));
125 if(strchr(m->mflags, 'd')) m->flags |= Fdel;
126 if(strchr(m->mflags, 's')) m->flags |= Fseen;
127 if(strchr(m->mflags, 'a')) m->flags |= Fresp;
130 if(tmparse(&tm, Datefmt, m->date, nil, nil) != nil)
131 m->time = tmnorm(&tm);
133 if(m->messageid != nil)
134 m->hash = strhash(m->messageid);
149 dpath = estrjoin(mbox.path, m->name, nil);
150 dfd = open(dpath, OREAD);
155 n = dirreadall(dfd, &d);
158 sysfatal("%s read: %r", mbox.path);
161 for(i = 0; i < n; i++){
162 if(d[i].qid.type != QTDIR)
165 apath = estrjoin(m->name, d[i].name, nil);
170 if(strncmp(a->type, "multipart/", strlen("multipart/")) == 0){
176 if(m->nparts >= m->xparts)
177 m->parts = erealloc(m->parts, (2 + m->nparts*2)*sizeof(Mesg*));
178 m->parts[m->nparts++] = a;
179 if(m->body == nil && strcmp(a->type, "text/plain") == 0)
181 else if(m->body == nil && strcmp(a->type, "text/html") == 0)
200 procexecl(m->sync, "/bin/htmlfmt", "htmlfmt", "-a", "-cutf-8", m->path, nil);
204 htmlfmt(Mesg *m, char *path)
206 if(pipe(m->fd) == -1)
207 sysfatal("pipe: %r");
208 m->sync = chancreate(sizeof(ulong), 0);
210 procrfork(execfmt, m, Stack, RFNOTEG);
218 copy(Biobuf *wfd, Biobuf *rfd)
223 buf = emalloc(Bufsz);
225 n = Bread(rfd, buf, Bufsz);
228 if(Bwrite(wfd, buf, n) != n)
237 char *path, *home, *name, *suff;
242 if((wfd = bwinopen(m, "body", OWRITE)) == nil)
244 if(m->parent != nil || m->nchild != 0) {
245 Bprint(wfd, "Thread:");
246 if(m->parent && !(m->parent->state & Sdummy))
247 Bprint(wfd, " ↑ %s", m->parent->name);
248 for(i = 0; i < m->nchild; i++)
249 Bprint(wfd, " ↓ %s", m->child[i]->name);
252 Bprint(wfd, "From: %s\n", m->fromcolon);
253 Bprint(wfd, "To: %s\n", m->to);
254 Bprint(wfd, "Date: %s\n", m->date);
255 Bprint(wfd, "Subject: %s\n\n", m->subject);
257 rfd = mesgopenbody(m);
263 home = getenv("home");
266 for(i = 0; i < m->nparts; i++){
269 if(strncmp(a->name, m->name, strlen(m->name)) == 0)
270 name += strlen(m->name);
271 if(a->disposition != nil
272 && strcmp(a->disposition, "inline") == 0
273 && strcmp(a->type, "text/plain") == 0){
274 if(a == m || a == m->body)
276 Bprint(wfd, "\n===> %s (%s)\n", name, a->type);
277 path = estrjoin(mbox.path, a->name, "body", nil);
278 if((rfd = Bopen(path, OREAD)) != nil){
285 Bprint(wfd, "\n===> %s (%s)\n", name, a->type);
289 if((suff = strchr(name, '.')) == nil)
291 Bprint(wfd, "\tcp %s%sbody%s %s/%s\n", mbox.path, a->name, suff, home, name);
296 fprint(m->ctl, "clean\n");
301 reply(Mesg *m, char **f, int nf)
303 if(nf >= 1 && strcmp(f[0], "all") == 0)
304 compose(m->replyto, m, 1);
306 compose(m->replyto, m, 0);
310 delmesg(Mesg *m, char **, int nf)
313 fprint(2, "Delmesg: too many args\n");
322 markone(Mesg *m, char **f, int nf)
328 fprint(2, "Mark: invalid arguments");
332 if((flg = mesgflagparse(f[0], &add)) == -1){
333 fprint(2, "Mark: invalid flags %s\n", f[0]);
340 if(strlen(f[0]) != 0){
341 path = estrjoin(mbox.path, "/", m->name, "/flags", nil);
342 if((fd = open(path, OWRITE)) != -1){
353 mesgquit(Mesg *m, char **, int)
355 if(fprint(m->ctl, "del\n") == -1)
361 static Fn mesgfn[] = {
363 {"Delmesg", delmesg},
383 m->qnext = mbox.openmesg;
386 path = estrjoin(mbox.path, m->name, nil);
390 wintagwrite(m, "Reply all Delmesg Save ");
392 fprint(m->ctl, "clean\n");
395 if(winevent(m, &ev) != 'M')
397 if(strcmp(ev.text, "Del") == 0)
402 if(matchmesg(m, ev.text))
403 mesgopen(ev.text, nil);
409 if((nf = tokenize(ev.text, f, nelem(f))) == 0)
411 for(p = mesgfn; p->fn != nil; p++){
412 if(strcmp(p->name, f[0]) == 0 && p->fn != nil){
413 p->fn(m, &f[1], nf - 1);
422 for(pm = &mbox.openmesg; *pm != nil; pm = &(*pm)->qnext)
435 mesgflagparse(char *fstr, int *add)
440 *add = (*fstr == '+');
441 if(*fstr == '-' || *fstr == '+')
443 for(; *fstr; fstr++){
453 memcpy(fstr, fstr +1, strlen(fstr));
456 fprint(2, "unknown flag %c", *fstr);
464 mesgpath2name(char *buf, int nbuf, char *name)
469 n = strlen(mbox.path);
470 if(strncmp(name, mbox.path, n) == 0)
471 e = strecpy(buf, buf+nbuf-2, name + n);
473 e = strecpy(buf, buf+nbuf-2, name);
474 if((p = strchr(buf, '/')) == nil)
481 mesgmatch(Mesg *m, char *name, char *digest)
483 if(!(m->state & Sdummy) && strcmp(m->name, name) == 0)
484 return digest == nil || strcmp(m->digest, digest) == 0;
489 mesglookup(char *name, char *digest)
494 mesgpath2name(buf, sizeof(buf), name);
495 for(i = 0; i < mbox.nmesg; i++)
496 if(mesgmatch(mbox.mesg[i], buf, digest))
502 mesgopen(char *name, char *digest)
508 m = mesglookup(name, digest);
509 if(m == nil || (m->state & Sopen))
512 assert(!(m->state & Sdummy));
514 if(!(m->flags & Fseen)){
516 path = estrjoin(mbox.path, "/", m->name, "/flags", nil);
517 if((fd = open(path, OWRITE)) != -1){
524 threadcreate(mesgmain, m, Stack);
529 mesgopenbody(Mesg *m)
536 path = estrjoin(mbox.path, b->name, "body", nil);
537 if(strcmp(b->type, "text/html") == 0)
538 rfd = htmlfmt(m, path);
540 rfd = open(path, OREAD);
544 return Bfdopen(rfd, OREAD);