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