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