#include #include #include #include #include "imap4d.h" char *fetchPartNames[FPMax] = { "", "HEADER", "HEADER.FIELDS", "HEADER.FIELDS.NOT", "MIME", "TEXT", }; /* * implicitly set the \seen flag. done in a separate pass * so the .imp file doesn't need to be open while the * messages are sent to the client. */ int fetchSeen(Box *box, Msg *m, int uids, void *vf) { Fetch *f; USED(uids); if(m->expunged) return uids; for(f = vf; f != nil; f = f->next){ switch(f->op){ case FRfc822: case FRfc822Text: case FBodySect: msgSeen(box, m); goto breakout; } } breakout: return 1; } /* * fetch messages * * imap4 body[] requestes get translated to upas/fs files as follows * body[id.header] == id/rawheader file + extra \r\n * body[id.text] == id/rawbody * body[id.mime] == id/mimeheader + extra \r\n * body[id] === body[id.header] + body[id.text] */ int fetchMsg(Box *, Msg *m, int uids, void *vf) { Tm tm; Fetch *f; char *sep; int todo; if(m->expunged) return uids; todo = 0; for(f = vf; f != nil; f = f->next){ switch(f->op){ case FFlags: todo = 1; break; case FUid: todo = 1; break; case FInternalDate: case FEnvelope: case FRfc822: case FRfc822Head: case FRfc822Size: case FRfc822Text: case FBodySect: case FBodyPeek: case FBody: case FBodyStruct: todo = 1; if(!msgStruct(m, 1)){ msgDead(m); return uids; } break; default: bye("bad implementation of fetch"); return 0; } } if(m->expunged) return uids; if(!todo) return 1; /* * note: it is allowed to send back the responses one at a time * rather than all together. this is exploited to send flags elsewhere. */ Bprint(&bout, "* %lud FETCH (", m->seq); sep = ""; if(uids){ Bprint(&bout, "UID %lud", m->uid); sep = " "; } for(f = vf; f != nil; f = f->next){ switch(f->op){ default: bye("bad implementation of fetch"); break; case FFlags: Bprint(&bout, "%sFLAGS (", sep); writeFlags(&bout, m, 1); Bprint(&bout, ")"); break; case FUid: if(uids) continue; Bprint(&bout, "%sUID %lud", sep, m->uid); break; case FEnvelope: Bprint(&bout, "%sENVELOPE ", sep); fetchEnvelope(m); break; case FInternalDate: Bprint(&bout, "%sINTERNALDATE ", sep); Bimapdate(&bout, date2tm(&tm, m->unixDate)); break; case FBody: Bprint(&bout, "%sBODY ", sep); fetchBodyStruct(m, &m->head, 0); break; case FBodyStruct: Bprint(&bout, "%sBODYSTRUCTURE ", sep); fetchBodyStruct(m, &m->head, 1); break; case FRfc822Size: Bprint(&bout, "%sRFC822.SIZE %lud", sep, msgSize(m)); break; case FRfc822: f->part = FPAll; Bprint(&bout, "%sRFC822", sep); fetchBody(m, f); break; case FRfc822Head: f->part = FPHead; Bprint(&bout, "%sRFC822.HEADER", sep); fetchBody(m, f); break; case FRfc822Text: f->part = FPText; Bprint(&bout, "%sRFC822.TEXT", sep); fetchBody(m, f); break; case FBodySect: case FBodyPeek: Bprint(&bout, "%sBODY", sep); fetchBody(fetchSect(m, f), f); break; } sep = " "; } Bprint(&bout, ")\r\n"); return 1; } /* * print out section, part, headers; * find and return message section */ Msg * fetchSect(Msg *m, Fetch *f) { Bputc(&bout, '['); BNList(&bout, f->sect, "."); if(f->part != FPAll){ if(f->sect != nil) Bputc(&bout, '.'); Bprint(&bout, "%s", fetchPartNames[f->part]); if(f->hdrs != nil){ Bprint(&bout, " ("); BSList(&bout, f->hdrs, " "); Bputc(&bout, ')'); } } Bprint(&bout, "]"); return findMsgSect(m, f->sect); } /* * actually return the body pieces */ void fetchBody(Msg *m, Fetch *f) { Pair p; char *s, *t, *e, buf[BufSize + 2]; ulong n, start, stop, pos; int fd, nn; if(m == nil){ fetchBodyStr(f, "", 0); return; } switch(f->part){ case FPHeadFields: case FPHeadFieldsNot: n = m->head.size + 3; s = emalloc(n); n = selectFields(s, n, m->head.buf, f->hdrs, f->part == FPHeadFields); fetchBodyStr(f, s, n); free(s); return; case FPHead: fetchBodyStr(f, m->head.buf, m->head.size); return; case FPMime: fetchBodyStr(f, m->mime.buf, m->mime.size); return; case FPAll: fd = msgFile(m, "rawbody"); if(fd < 0){ msgDead(m); fetchBodyStr(f, "", 0); return; } p = fetchBodyPart(f, msgSize(m)); start = p.start; if(start < m->head.size){ stop = p.stop; if(stop > m->head.size) stop = m->head.size; Bwrite(&bout, &m->head.buf[start], stop - start); start = 0; stop = p.stop; if(stop <= m->head.size){ close(fd); return; } }else start -= m->head.size; stop = p.stop - m->head.size; break; case FPText: fd = msgFile(m, "rawbody"); if(fd < 0){ msgDead(m); fetchBodyStr(f, "", 0); return; } p = fetchBodyPart(f, m->size); start = p.start; stop = p.stop; break; default: fetchBodyStr(f, "", 0); return; } /* * read in each block, convert \n without \r to \r\n. * this means partial fetch requires fetching everything * through stop, since we don't know how many \r's will be added */ buf[0] = ' '; for(pos = 0; pos < stop; ){ n = BufSize; if(n > stop - pos) n = stop - pos; n = read(fd, &buf[1], n); if(n <= 0){ fetchBodyFill(stop - pos); break; } e = &buf[n + 1]; *e = '\0'; for(s = &buf[1]; s < e && pos < stop; s = t + 1){ t = memchr(s, '\n', e - s); if(t == nil) t = e; n = t - s; if(pos < start){ if(pos + n <= start){ s = t; pos += n; }else{ s += start - pos; pos = start; } n = t - s; } nn = n; if(pos + nn > stop) nn = stop - pos; if(Bwrite(&bout, s, nn) != nn) writeErr(); pos += n; if(*t == '\n'){ if(t[-1] != '\r'){ if(pos >= start && pos < stop) Bputc(&bout, '\r'); pos++; } if(pos >= start && pos < stop) Bputc(&bout, '\n'); pos++; } } buf[0] = e[-1]; } close(fd); } /* * resolve the actual bounds of any partial fetch, * and print out the bounds & size of string returned */ Pair fetchBodyPart(Fetch *f, ulong size) { Pair p; ulong start, stop; start = 0; stop = size; if(f->partial){ start = f->start; if(start > size) start = size; stop = start + f->size; if(stop > size) stop = size; Bprint(&bout, "<%lud>", start); } Bprint(&bout, " {%lud}\r\n", stop - start); p.start = start; p.stop = stop; return p; } /* * something went wrong fetching data * produce fill bytes for what we've committed to produce */ void fetchBodyFill(ulong n) { while(n-- > 0) if(Bputc(&bout, ' ') < 0) writeErr(); } /* * return a simple string */ void fetchBodyStr(Fetch *f, char *buf, ulong size) { Pair p; p = fetchBodyPart(f, size); Bwrite(&bout, &buf[p.start], p.stop-p.start); } char* printnlist(NList *sect) { static char buf[100]; char *p; for(p= buf; sect; sect=sect->next){ p += sprint(p, "%ld", sect->n); if(sect->next) *p++ = '.'; } *p = '\0'; return buf; } /* * find the numbered sub-part of the message */ Msg* findMsgSect(Msg *m, NList *sect) { ulong id; for(; sect != nil; sect = sect->next){ id = sect->n; #ifdef HACK /* HACK to solve extra level of structure not visible from upas/fs */ if(m->kids == 0 && id == 1 && sect->next == nil){ if(m->mime.type->s && strcmp(m->mime.type->s, "message")==0) if(m->mime.type->t && strcmp(m->mime.type->t, "rfc822")==0) if(m->head.type->s && strcmp(m->head.type->s, "text")==0) if(m->head.type->t && strcmp(m->head.type->t, "plain")==0) break; } /* end of HACK */ #endif HACK for(m = m->kids; m != nil; m = m->next) if(m->id == id) break; if(m == nil) return nil; } return m; } void fetchEnvelope(Msg *m) { Tm tm; Bputc(&bout, '('); Brfc822date(&bout, date2tm(&tm, m->info[IDate])); Bputc(&bout, ' '); Bimapstr(&bout, m->info[ISubject]); Bputc(&bout, ' '); Bimapaddr(&bout, m->from); Bputc(&bout, ' '); Bimapaddr(&bout, m->sender); Bputc(&bout, ' '); Bimapaddr(&bout, m->replyTo); Bputc(&bout, ' '); Bimapaddr(&bout, m->to); Bputc(&bout, ' '); Bimapaddr(&bout, m->cc); Bputc(&bout, ' '); Bimapaddr(&bout, m->bcc); Bputc(&bout, ' '); Bimapstr(&bout, m->info[IInReplyTo]); Bputc(&bout, ' '); Bimapstr(&bout, m->info[IMessageId]); Bputc(&bout, ')'); } void fetchBodyStruct(Msg *m, Header *h, int extensions) { Msg *k; ulong len; if(msgIsMulti(h)){ Bputc(&bout, '('); for(k = m->kids; k != nil; k = k->next) fetchBodyStruct(k, &k->mime, extensions); Bputc(&bout, ' '); Bimapstr(&bout, h->type->t); if(extensions){ Bputc(&bout, ' '); BimapMimeParams(&bout, h->type->next); fetchStructExt(h); } Bputc(&bout, ')'); return; } Bputc(&bout, '('); if(h->type != nil){ Bimapstr(&bout, h->type->s); Bputc(&bout, ' '); Bimapstr(&bout, h->type->t); Bputc(&bout, ' '); BimapMimeParams(&bout, h->type->next); }else Bprint(&bout, "\"text\" \"plain\" NIL"); Bputc(&bout, ' '); if(h->id != nil) Bimapstr(&bout, h->id->s); else Bprint(&bout, "NIL"); Bputc(&bout, ' '); if(h->description != nil) Bimapstr(&bout, h->description->s); else Bprint(&bout, "NIL"); Bputc(&bout, ' '); if(h->encoding != nil) Bimapstr(&bout, h->encoding->s); else Bprint(&bout, "NIL"); /* * this is so strange: return lengths for a body[text] response, * except in the case of a multipart message, when return lengths for a body[] response */ len = m->size; if(h == &m->mime) len += m->head.size; Bprint(&bout, " %lud", len); len = m->lines; if(h == &m->mime) len += m->head.lines; if(h->type == nil || cistrcmp(h->type->s, "text") == 0){ Bprint(&bout, " %lud", len); }else if(msgIsRfc822(h)){ Bputc(&bout, ' '); k = m; if(h != &m->mime) k = m->kids; if(k == nil) Bprint(&bout, "(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL) (\"text\" \"plain\" NIL NIL NIL NIL 0 0) 0"); else{ fetchEnvelope(k); Bputc(&bout, ' '); fetchBodyStruct(k, &k->head, extensions); Bprint(&bout, " %lud", len); } } if(extensions){ Bputc(&bout, ' '); /* * don't have the md5 laying around, * since the header & body have added newlines. */ Bprint(&bout, "NIL"); fetchStructExt(h); } Bputc(&bout, ')'); } /* * common part of bodystructure extensions */ void fetchStructExt(Header *h) { Bputc(&bout, ' '); if(h->disposition != nil){ Bputc(&bout, '('); Bimapstr(&bout, h->disposition->s); Bputc(&bout, ' '); BimapMimeParams(&bout, h->disposition->next); Bputc(&bout, ')'); }else Bprint(&bout, "NIL"); Bputc(&bout, ' '); if(h->language != nil){ if(h->language->next != nil) BimapMimeParams(&bout, h->language->next); else Bimapstr(&bout, h->language->s); }else Bprint(&bout, "NIL"); } int BimapMimeParams(Biobuf *b, MimeHdr *mh) { char *sep; int n; if(mh == nil) return Bprint(b, "NIL"); n = Bputc(b, '('); sep = ""; for(; mh != nil; mh = mh->next){ n += Bprint(b, sep); n += Bimapstr(b, mh->s); n += Bputc(b, ' '); n += Bimapstr(b, mh->t); sep = " "; } n += Bputc(b, ')'); return n; } /* * print a list of addresses; * each address is printed as '(' personalName AtDomainList mboxName hostName ')' * the AtDomainList is always NIL */ int Bimapaddr(Biobuf *b, MAddr *a) { char *host, *sep; int n; if(a == nil) return Bprint(b, "NIL"); n = Bputc(b, '('); sep = ""; for(; a != nil; a = a->next){ n += Bprint(b, "%s(", sep); n += Bimapstr(b, a->personal); n += Bprint(b," NIL "); n += Bimapstr(b, a->box); n += Bputc(b, ' '); /* * can't send NIL as hostName, since that is code for a group */ host = a->host; if(host == nil) host = ""; n += Bimapstr(b, host); n += Bputc(b, ')'); sep = " "; } n += Bputc(b, ')'); return n; }