1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include "imap4d.h" 5 6 char *fetchPartNames[FPMax] = 7 { 8 "", 9 "HEADER", 10 "HEADER.FIELDS", 11 "HEADER.FIELDS.NOT", 12 "MIME", 13 "TEXT", 14 }; 15 16 /* 17 * implicitly set the \seen flag. done in a separate pass 18 * so the .imp file doesn't need to be open while the 19 * messages are sent to the client. 20 */ 21 int 22 fetchSeen(Box *box, Msg *m, int uids, void *vf) 23 { 24 Fetch *f; 25 26 USED(uids); 27 28 if(m->expunged) 29 return uids; 30 for(f = vf; f != nil; f = f->next){ 31 switch(f->op){ 32 case FRfc822: 33 case FRfc822Text: 34 case FBodySect: 35 msgSeen(box, m); 36 goto breakout; 37 } 38 } 39 breakout: 40 41 return 1; 42 } 43 44 /* 45 * fetch messages 46 * 47 * imap4 body[] requestes get translated to upas/fs files as follows 48 * body[id.header] == id/rawheader file + extra \r\n 49 * body[id.text] == id/rawbody 50 * body[id.mime] == id/mimeheader + extra \r\n 51 * body[id] === body[id.header] + body[id.text] 52 */ 53 int 54 fetchMsg(Box *box, Msg *m, int uids, void *vf) 55 { 56 Tm tm; 57 Fetch *f; 58 char *sep; 59 int todo; 60 61 if(m->expunged) 62 return uids; 63 64 todo = 0; 65 for(f = vf; f != nil; f = f->next){ 66 switch(f->op){ 67 case FFlags: 68 /* 69 * flags get sent with status update 70 */ 71 box->sendFlags = 1; 72 m->sendFlags = 1; 73 break; 74 case FUid: 75 todo = 1; 76 break; 77 case FInternalDate: 78 case FEnvelope: 79 case FRfc822: 80 case FRfc822Head: 81 case FRfc822Size: 82 case FRfc822Text: 83 case FBodySect: 84 case FBodyPeek: 85 case FBody: 86 case FBodyStruct: 87 todo = 1; 88 if(!msgStruct(m, 1)){ 89 msgDead(m); 90 return uids; 91 } 92 break; 93 default: 94 bye("bad implementation of fetch"); 95 return 0; 96 } 97 } 98 99 if(m->expunged) 100 return uids; 101 if(!todo) 102 return 1; 103 104 /* 105 * note: it is allowed to send back the responses one at a time 106 * rather than all together. this is exploited to send flags elsewhere. 107 */ 108 Bprint(&bout, "* %lud fetch (", m->seq); 109 sep = ""; 110 if(uids){ 111 Bprint(&bout, "uid %lud", m->uid); 112 sep = " "; 113 } 114 for(f = vf; f != nil; f = f->next){ 115 switch(f->op){ 116 default: 117 bye("bad implementation of fetch"); 118 break; 119 case FFlags: 120 continue; 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 /* 372 * find the numbered sub-part of the message 373 */ 374 Msg* 375 findMsgSect(Msg *m, NList *sect) 376 { 377 ulong id; 378 379 for(; sect != nil; sect = sect->next){ 380 id = sect->n; 381 for(m = m->kids; m != nil; m = m->next) 382 if(m->id == id) 383 break; 384 if(m == nil) 385 return nil; 386 } 387 return m; 388 } 389 390 void 391 fetchEnvelope(Msg *m) 392 { 393 Tm tm; 394 395 Bputc(&bout, '('); 396 Brfc822date(&bout, date2tm(&tm, m->info[IDate])); 397 Bputc(&bout, ' '); 398 Bimapstr(&bout, m->info[ISubject]); 399 Bputc(&bout, ' '); 400 Bimapaddr(&bout, m->from); 401 Bputc(&bout, ' '); 402 Bimapaddr(&bout, m->sender); 403 Bputc(&bout, ' '); 404 Bimapaddr(&bout, m->replyTo); 405 Bputc(&bout, ' '); 406 Bimapaddr(&bout, m->to); 407 Bputc(&bout, ' '); 408 Bimapaddr(&bout, m->cc); 409 Bputc(&bout, ' '); 410 Bimapaddr(&bout, m->bcc); 411 Bputc(&bout, ' '); 412 Bimapstr(&bout, m->info[IInReplyTo]); 413 Bputc(&bout, ' '); 414 Bimapstr(&bout, m->info[IMessageId]); 415 Bputc(&bout, ')'); 416 } 417 418 void 419 fetchBodyStruct(Msg *m, Header *h, int extensions) 420 { 421 Msg *k; 422 ulong len; 423 424 if(msgIsMulti(h)){ 425 Bputc(&bout, '('); 426 for(k = m->kids; k != nil; k = k->next) 427 fetchBodyStruct(k, &k->mime, extensions); 428 429 Bputc(&bout, ' '); 430 Bimapstr(&bout, h->type->t); 431 432 if(extensions){ 433 Bputc(&bout, ' '); 434 BimapMimeParams(&bout, h->type->next); 435 fetchStructExt(h); 436 } 437 438 Bputc(&bout, ')'); 439 return; 440 } 441 442 Bputc(&bout, '('); 443 if(h->type != nil){ 444 Bimapstr(&bout, h->type->s); 445 Bputc(&bout, ' '); 446 Bimapstr(&bout, h->type->t); 447 Bputc(&bout, ' '); 448 BimapMimeParams(&bout, h->type->next); 449 }else 450 Bprint(&bout, "\"text\" \"plain\" NIL"); 451 452 Bputc(&bout, ' '); 453 if(h->id != nil) 454 Bimapstr(&bout, h->id->s); 455 else 456 Bprint(&bout, "NIL"); 457 458 Bputc(&bout, ' '); 459 if(h->description != nil) 460 Bimapstr(&bout, h->description->s); 461 else 462 Bprint(&bout, "NIL"); 463 464 Bputc(&bout, ' '); 465 if(h->encoding != nil) 466 Bimapstr(&bout, h->encoding->s); 467 else 468 Bprint(&bout, "NIL"); 469 470 /* 471 * this is so strange: return lengths for a body[text] response, 472 * except in the case of a multipart message, when return lengths for a body[] repsonse 473 */ 474 len = m->size; 475 if(h == &m->mime) 476 len += m->head.size; 477 Bprint(&bout, " %lud", len); 478 479 len = m->lines; 480 if(h == &m->mime) 481 len += m->head.lines; 482 483 if(h->type == nil || cistrcmp(h->type->s, "text") == 0){ 484 Bprint(&bout, " %lud", len); 485 }else if(msgIsRfc822(h)){ 486 Bputc(&bout, ' '); 487 k = m; 488 if(h != &m->mime) 489 k = m->kids; 490 if(k == nil) 491 Bprint(&bout, "(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL) (\"text\" \"plain\" NIL NIL NIL NIL 0 0) 0"); 492 else{ 493 fetchEnvelope(k); 494 Bputc(&bout, ' '); 495 fetchBodyStruct(k, &k->head, extensions); 496 Bprint(&bout, " %lud", len); 497 } 498 } 499 500 if(extensions){ 501 Bputc(&bout, ' '); 502 503 /* 504 * don't have the md5 laying around, 505 * since the header & body have added newlines. 506 */ 507 Bprint(&bout, "NIL"); 508 509 fetchStructExt(h); 510 } 511 Bputc(&bout, ')'); 512 } 513 514 /* 515 * common part of bodystructure extensions 516 */ 517 void 518 fetchStructExt(Header *h) 519 { 520 Bputc(&bout, ' '); 521 if(h->disposition != nil){ 522 Bputc(&bout, '('); 523 Bimapstr(&bout, h->disposition->s); 524 Bputc(&bout, ' '); 525 BimapMimeParams(&bout, h->disposition->next); 526 Bputc(&bout, ')'); 527 }else 528 Bprint(&bout, "NIL"); 529 Bputc(&bout, ' '); 530 if(h->language != nil){ 531 if(h->language->next != nil) 532 BimapMimeParams(&bout, h->language->next); 533 else 534 Bimapstr(&bout, h->language->s); 535 }else 536 Bprint(&bout, "NIL"); 537 } 538 539 int 540 BimapMimeParams(Biobuf *b, MimeHdr *mh) 541 { 542 char *sep; 543 int n; 544 545 if(mh == nil) 546 return Bprint(b, "NIL"); 547 548 n = Bputc(b, '('); 549 550 sep = ""; 551 for(; mh != nil; mh = mh->next){ 552 n += Bprint(b, sep); 553 n += Bimapstr(b, mh->s); 554 n += Bputc(b, ' '); 555 n += Bimapstr(b, mh->t); 556 sep = " "; 557 } 558 559 n += Bputc(b, ')'); 560 return n; 561 } 562 563 /* 564 * print a list of addresses; 565 * each address is printed as '(' personalName AtDomainList mboxName hostName ')' 566 * the AtDomainList is always NIL 567 */ 568 int 569 Bimapaddr(Biobuf *b, MAddr *a) 570 { 571 char *host, *sep; 572 int n; 573 574 if(a == nil) 575 return Bprint(b, "NIL"); 576 577 n = Bputc(b, '('); 578 sep = ""; 579 for(; a != nil; a = a->next){ 580 n += Bprint(b, "%s(", sep); 581 n += Bimapstr(b, a->personal); 582 n += Bprint(b," NIL "); 583 n += Bimapstr(b, a->box); 584 n += Bputc(b, ' '); 585 586 /* 587 * can't send NIL as hostName, since that is code for a group 588 */ 589 host = a->host; 590 if(host == nil) 591 host = ""; 592 n += Bimapstr(b, host); 593 594 n += Bputc(b, ')'); 595 sep = " "; 596 } 597 n += Bputc(b, ')'); 598 return n; 599 } 600