1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <auth.h> 5 #include "imap4d.h" 6 7 char *fetchPartNames[FPMax] = 8 { 9 "", 10 "HEADER", 11 "HEADER.FIELDS", 12 "HEADER.FIELDS.NOT", 13 "MIME", 14 "TEXT", 15 }; 16 17 /* 18 * implicitly set the \seen flag. done in a separate pass 19 * so the .imp file doesn't need to be open while the 20 * messages are sent to the client. 21 */ 22 int 23 fetchSeen(Box *box, Msg *m, int uids, void *vf) 24 { 25 Fetch *f; 26 27 USED(uids); 28 29 if(m->expunged) 30 return uids; 31 for(f = vf; f != nil; f = f->next){ 32 switch(f->op){ 33 case FRfc822: 34 case FRfc822Text: 35 case FBodySect: 36 msgSeen(box, m); 37 goto breakout; 38 } 39 } 40 breakout: 41 42 return 1; 43 } 44 45 /* 46 * fetch messages 47 * 48 * imap4 body[] requestes get translated to upas/fs files as follows 49 * body[id.header] == id/rawheader file + extra \r\n 50 * body[id.text] == id/rawbody 51 * body[id.mime] == id/mimeheader + extra \r\n 52 * body[id] === body[id.header] + body[id.text] 53 */ 54 int 55 fetchMsg(Box *, Msg *m, int uids, void *vf) 56 { 57 Tm tm; 58 Fetch *f; 59 char *sep; 60 int todo; 61 62 if(m->expunged) 63 return uids; 64 65 todo = 0; 66 for(f = vf; f != nil; f = f->next){ 67 switch(f->op){ 68 case FFlags: 69 todo = 1; 70 break; 71 case FUid: 72 todo = 1; 73 break; 74 case FInternalDate: 75 case FEnvelope: 76 case FRfc822: 77 case FRfc822Head: 78 case FRfc822Size: 79 case FRfc822Text: 80 case FBodySect: 81 case FBodyPeek: 82 case FBody: 83 case FBodyStruct: 84 todo = 1; 85 if(!msgStruct(m, 1)){ 86 msgDead(m); 87 return uids; 88 } 89 break; 90 default: 91 bye("bad implementation of fetch"); 92 return 0; 93 } 94 } 95 96 if(m->expunged) 97 return uids; 98 if(!todo) 99 return 1; 100 101 /* 102 * note: it is allowed to send back the responses one at a time 103 * rather than all together. this is exploited to send flags elsewhere. 104 */ 105 Bprint(&bout, "* %lud FETCH (", m->seq); 106 sep = ""; 107 if(uids){ 108 Bprint(&bout, "UID %lud", m->uid); 109 sep = " "; 110 } 111 for(f = vf; f != nil; f = f->next){ 112 switch(f->op){ 113 default: 114 bye("bad implementation of fetch"); 115 break; 116 case FFlags: 117 Bprint(&bout, "%sFLAGS (", sep); 118 writeFlags(&bout, m, 1); 119 Bprint(&bout, ")"); 120 break; 121 case FUid: 122 if(uids) 123 continue; 124 Bprint(&bout, "%sUID %lud", sep, m->uid); 125 break; 126 case FEnvelope: 127 Bprint(&bout, "%sENVELOPE ", sep); 128 fetchEnvelope(m); 129 break; 130 case FInternalDate: 131 Bprint(&bout, "%sINTERNALDATE ", sep); 132 Bimapdate(&bout, date2tm(&tm, m->unixDate)); 133 break; 134 case FBody: 135 Bprint(&bout, "%sBODY ", sep); 136 fetchBodyStruct(m, &m->head, 0); 137 break; 138 case FBodyStruct: 139 Bprint(&bout, "%sBODYSTRUCTURE ", sep); 140 fetchBodyStruct(m, &m->head, 1); 141 break; 142 case FRfc822Size: 143 Bprint(&bout, "%sRFC822.SIZE %lud", sep, msgSize(m)); 144 break; 145 case FRfc822: 146 f->part = FPAll; 147 Bprint(&bout, "%sRFC822", sep); 148 fetchBody(m, f); 149 break; 150 case FRfc822Head: 151 f->part = FPHead; 152 Bprint(&bout, "%sRFC822.HEADER", sep); 153 fetchBody(m, f); 154 break; 155 case FRfc822Text: 156 f->part = FPText; 157 Bprint(&bout, "%sRFC822.TEXT", sep); 158 fetchBody(m, f); 159 break; 160 case FBodySect: 161 case FBodyPeek: 162 Bprint(&bout, "%sBODY", sep); 163 fetchBody(fetchSect(m, f), f); 164 break; 165 } 166 sep = " "; 167 } 168 Bprint(&bout, ")\r\n"); 169 170 return 1; 171 } 172 173 /* 174 * print out section, part, headers; 175 * find and return message section 176 */ 177 Msg * 178 fetchSect(Msg *m, Fetch *f) 179 { 180 Bputc(&bout, '['); 181 BNList(&bout, f->sect, "."); 182 if(f->part != FPAll){ 183 if(f->sect != nil) 184 Bputc(&bout, '.'); 185 Bprint(&bout, "%s", fetchPartNames[f->part]); 186 if(f->hdrs != nil){ 187 Bprint(&bout, " ("); 188 BSList(&bout, f->hdrs, " "); 189 Bputc(&bout, ')'); 190 } 191 } 192 Bprint(&bout, "]"); 193 return findMsgSect(m, f->sect); 194 } 195 196 /* 197 * actually return the body pieces 198 */ 199 void 200 fetchBody(Msg *m, Fetch *f) 201 { 202 Pair p; 203 char *s, *t, *e, buf[BufSize + 2]; 204 ulong n, start, stop, pos; 205 int fd, nn; 206 207 if(m == nil){ 208 fetchBodyStr(f, "", 0); 209 return; 210 } 211 switch(f->part){ 212 case FPHeadFields: 213 case FPHeadFieldsNot: 214 n = m->head.size + 3; 215 s = emalloc(n); 216 n = selectFields(s, n, m->head.buf, f->hdrs, f->part == FPHeadFields); 217 fetchBodyStr(f, s, n); 218 free(s); 219 return; 220 case FPHead: 221 fetchBodyStr(f, m->head.buf, m->head.size); 222 return; 223 case FPMime: 224 fetchBodyStr(f, m->mime.buf, m->mime.size); 225 return; 226 case FPAll: 227 fd = msgFile(m, "rawbody"); 228 if(fd < 0){ 229 msgDead(m); 230 fetchBodyStr(f, "", 0); 231 return; 232 } 233 p = fetchBodyPart(f, msgSize(m)); 234 start = p.start; 235 if(start < m->head.size){ 236 stop = p.stop; 237 if(stop > m->head.size) 238 stop = m->head.size; 239 Bwrite(&bout, &m->head.buf[start], stop - start); 240 start = 0; 241 stop = p.stop; 242 if(stop <= m->head.size){ 243 close(fd); 244 return; 245 } 246 }else 247 start -= m->head.size; 248 stop = p.stop - m->head.size; 249 break; 250 case FPText: 251 fd = msgFile(m, "rawbody"); 252 if(fd < 0){ 253 msgDead(m); 254 fetchBodyStr(f, "", 0); 255 return; 256 } 257 p = fetchBodyPart(f, m->size); 258 start = p.start; 259 stop = p.stop; 260 break; 261 default: 262 fetchBodyStr(f, "", 0); 263 return; 264 } 265 266 /* 267 * read in each block, convert \n without \r to \r\n. 268 * this means partial fetch requires fetching everything 269 * through stop, since we don't know how many \r's will be added 270 */ 271 buf[0] = ' '; 272 for(pos = 0; pos < stop; ){ 273 n = BufSize; 274 if(n > stop - pos) 275 n = stop - pos; 276 n = read(fd, &buf[1], n); 277 if(n <= 0){ 278 fetchBodyFill(stop - pos); 279 break; 280 } 281 e = &buf[n + 1]; 282 *e = '\0'; 283 for(s = &buf[1]; s < e && pos < stop; s = t + 1){ 284 t = memchr(s, '\n', e - s); 285 if(t == nil) 286 t = e; 287 n = t - s; 288 if(pos < start){ 289 if(pos + n <= start){ 290 s = t; 291 pos += n; 292 }else{ 293 s += start - pos; 294 pos = start; 295 } 296 n = t - s; 297 } 298 nn = n; 299 if(pos + nn > stop) 300 nn = stop - pos; 301 if(Bwrite(&bout, s, nn) != nn) 302 writeErr(); 303 pos += n; 304 if(*t == '\n'){ 305 if(t[-1] != '\r'){ 306 if(pos >= start && pos < stop) 307 Bputc(&bout, '\r'); 308 pos++; 309 } 310 if(pos >= start && pos < stop) 311 Bputc(&bout, '\n'); 312 pos++; 313 } 314 } 315 buf[0] = e[-1]; 316 } 317 close(fd); 318 } 319 320 /* 321 * resolve the actual bounds of any partial fetch, 322 * and print out the bounds & size of string returned 323 */ 324 Pair 325 fetchBodyPart(Fetch *f, ulong size) 326 { 327 Pair p; 328 ulong start, stop; 329 330 start = 0; 331 stop = size; 332 if(f->partial){ 333 start = f->start; 334 if(start > size) 335 start = size; 336 stop = start + f->size; 337 if(stop > size) 338 stop = size; 339 Bprint(&bout, "<%lud>", start); 340 } 341 Bprint(&bout, " {%lud}\r\n", stop - start); 342 p.start = start; 343 p.stop = stop; 344 return p; 345 } 346 347 /* 348 * something went wrong fetching data 349 * produce fill bytes for what we've committed to produce 350 */ 351 void 352 fetchBodyFill(ulong n) 353 { 354 while(n-- > 0) 355 if(Bputc(&bout, ' ') < 0) 356 writeErr(); 357 } 358 359 /* 360 * return a simple string 361 */ 362 void 363 fetchBodyStr(Fetch *f, char *buf, ulong size) 364 { 365 Pair p; 366 367 p = fetchBodyPart(f, size); 368 Bwrite(&bout, &buf[p.start], p.stop-p.start); 369 } 370 371 char* 372 printnlist(NList *sect) 373 { 374 static char buf[100]; 375 char *p; 376 377 for(p= buf; sect; sect=sect->next){ 378 p += sprint(p, "%ld", sect->n); 379 if(sect->next) 380 *p++ = '.'; 381 } 382 *p = '\0'; 383 return buf; 384 } 385 386 /* 387 * find the numbered sub-part of the message 388 */ 389 Msg* 390 findMsgSect(Msg *m, NList *sect) 391 { 392 ulong id; 393 394 for(; sect != nil; sect = sect->next){ 395 id = sect->n; 396 #ifdef HACK 397 /* HACK to solve extra level of structure not visible from upas/fs */ 398 if(m->kids == 0 && id == 1 && sect->next == nil){ 399 if(m->mime.type->s && strcmp(m->mime.type->s, "message")==0) 400 if(m->mime.type->t && strcmp(m->mime.type->t, "rfc822")==0) 401 if(m->head.type->s && strcmp(m->head.type->s, "text")==0) 402 if(m->head.type->t && strcmp(m->head.type->t, "plain")==0) 403 break; 404 } 405 /* end of HACK */ 406 #endif HACK 407 for(m = m->kids; m != nil; m = m->next) 408 if(m->id == id) 409 break; 410 if(m == nil) 411 return nil; 412 } 413 return m; 414 } 415 416 void 417 fetchEnvelope(Msg *m) 418 { 419 Tm tm; 420 421 Bputc(&bout, '('); 422 Brfc822date(&bout, date2tm(&tm, m->info[IDate])); 423 Bputc(&bout, ' '); 424 Bimapstr(&bout, m->info[ISubject]); 425 Bputc(&bout, ' '); 426 Bimapaddr(&bout, m->from); 427 Bputc(&bout, ' '); 428 Bimapaddr(&bout, m->sender); 429 Bputc(&bout, ' '); 430 Bimapaddr(&bout, m->replyTo); 431 Bputc(&bout, ' '); 432 Bimapaddr(&bout, m->to); 433 Bputc(&bout, ' '); 434 Bimapaddr(&bout, m->cc); 435 Bputc(&bout, ' '); 436 Bimapaddr(&bout, m->bcc); 437 Bputc(&bout, ' '); 438 Bimapstr(&bout, m->info[IInReplyTo]); 439 Bputc(&bout, ' '); 440 Bimapstr(&bout, m->info[IMessageId]); 441 Bputc(&bout, ')'); 442 } 443 444 void 445 fetchBodyStruct(Msg *m, Header *h, int extensions) 446 { 447 Msg *k; 448 ulong len; 449 450 if(msgIsMulti(h)){ 451 Bputc(&bout, '('); 452 for(k = m->kids; k != nil; k = k->next) 453 fetchBodyStruct(k, &k->mime, extensions); 454 455 Bputc(&bout, ' '); 456 Bimapstr(&bout, h->type->t); 457 458 if(extensions){ 459 Bputc(&bout, ' '); 460 BimapMimeParams(&bout, h->type->next); 461 fetchStructExt(h); 462 } 463 464 Bputc(&bout, ')'); 465 return; 466 } 467 468 Bputc(&bout, '('); 469 if(h->type != nil){ 470 Bimapstr(&bout, h->type->s); 471 Bputc(&bout, ' '); 472 Bimapstr(&bout, h->type->t); 473 Bputc(&bout, ' '); 474 BimapMimeParams(&bout, h->type->next); 475 }else 476 Bprint(&bout, "\"text\" \"plain\" NIL"); 477 478 Bputc(&bout, ' '); 479 if(h->id != nil) 480 Bimapstr(&bout, h->id->s); 481 else 482 Bprint(&bout, "NIL"); 483 484 Bputc(&bout, ' '); 485 if(h->description != nil) 486 Bimapstr(&bout, h->description->s); 487 else 488 Bprint(&bout, "NIL"); 489 490 Bputc(&bout, ' '); 491 if(h->encoding != nil) 492 Bimapstr(&bout, h->encoding->s); 493 else 494 Bprint(&bout, "NIL"); 495 496 /* 497 * this is so strange: return lengths for a body[text] response, 498 * except in the case of a multipart message, when return lengths for a body[] response 499 */ 500 len = m->size; 501 if(h == &m->mime) 502 len += m->head.size; 503 Bprint(&bout, " %lud", len); 504 505 len = m->lines; 506 if(h == &m->mime) 507 len += m->head.lines; 508 509 if(h->type == nil || cistrcmp(h->type->s, "text") == 0){ 510 Bprint(&bout, " %lud", len); 511 }else if(msgIsRfc822(h)){ 512 Bputc(&bout, ' '); 513 k = m; 514 if(h != &m->mime) 515 k = m->kids; 516 if(k == nil) 517 Bprint(&bout, "(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL) (\"text\" \"plain\" NIL NIL NIL NIL 0 0) 0"); 518 else{ 519 fetchEnvelope(k); 520 Bputc(&bout, ' '); 521 fetchBodyStruct(k, &k->head, extensions); 522 Bprint(&bout, " %lud", len); 523 } 524 } 525 526 if(extensions){ 527 Bputc(&bout, ' '); 528 529 /* 530 * don't have the md5 laying around, 531 * since the header & body have added newlines. 532 */ 533 Bprint(&bout, "NIL"); 534 535 fetchStructExt(h); 536 } 537 Bputc(&bout, ')'); 538 } 539 540 /* 541 * common part of bodystructure extensions 542 */ 543 void 544 fetchStructExt(Header *h) 545 { 546 Bputc(&bout, ' '); 547 if(h->disposition != nil){ 548 Bputc(&bout, '('); 549 Bimapstr(&bout, h->disposition->s); 550 Bputc(&bout, ' '); 551 BimapMimeParams(&bout, h->disposition->next); 552 Bputc(&bout, ')'); 553 }else 554 Bprint(&bout, "NIL"); 555 Bputc(&bout, ' '); 556 if(h->language != nil){ 557 if(h->language->next != nil) 558 BimapMimeParams(&bout, h->language->next); 559 else 560 Bimapstr(&bout, h->language->s); 561 }else 562 Bprint(&bout, "NIL"); 563 } 564 565 int 566 BimapMimeParams(Biobuf *b, MimeHdr *mh) 567 { 568 char *sep; 569 int n; 570 571 if(mh == nil) 572 return Bprint(b, "NIL"); 573 574 n = Bputc(b, '('); 575 576 sep = ""; 577 for(; mh != nil; mh = mh->next){ 578 n += Bprint(b, sep); 579 n += Bimapstr(b, mh->s); 580 n += Bputc(b, ' '); 581 n += Bimapstr(b, mh->t); 582 sep = " "; 583 } 584 585 n += Bputc(b, ')'); 586 return n; 587 } 588 589 /* 590 * print a list of addresses; 591 * each address is printed as '(' personalName AtDomainList mboxName hostName ')' 592 * the AtDomainList is always NIL 593 */ 594 int 595 Bimapaddr(Biobuf *b, MAddr *a) 596 { 597 char *host, *sep; 598 int n; 599 600 if(a == nil) 601 return Bprint(b, "NIL"); 602 603 n = Bputc(b, '('); 604 sep = ""; 605 for(; a != nil; a = a->next){ 606 n += Bprint(b, "%s(", sep); 607 n += Bimapstr(b, a->personal); 608 n += Bprint(b," NIL "); 609 n += Bimapstr(b, a->box); 610 n += Bputc(b, ' '); 611 612 /* 613 * can't send NIL as hostName, since that is code for a group 614 */ 615 host = a->host; 616 if(host == nil) 617 host = ""; 618 n += Bimapstr(b, host); 619 620 n += Bputc(b, ')'); 621 sep = " "; 622 } 623 n += Bputc(b, ')'); 624 return n; 625 } 626