operator-fetch.cc (18610B)
1 /* -*- Mode: c++; -*- */ 2 /* -------------------------------------------------------------------- 3 * Filename: 4 * operator-fetch.cc 5 * 6 * Description: 7 * Implementation of the FETCH command. 8 * 9 * Authors: 10 * Andreas Aardal Hanssen <andreas-binc curly bincimap spot org> 11 * 12 * Bugs: 13 * 14 * ChangeLog: 15 * 16 * -------------------------------------------------------------------- 17 * Copyright 2002-2005 Andreas Aardal Hanssen 18 * 19 * This program is free software; you can redistribute it and/or modify 20 * it under the terms of the GNU General Public License as published by 21 * the Free Software Foundation; either version 2 of the License, or 22 * (at your option) any later version. 23 * 24 * This program is distributed in the hope that it will be useful, 25 * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 * GNU General Public License for more details. 28 * 29 * You should have received a copy of the GNU General Public License 30 * along with this program; if not, write to the Free Software 31 * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. 32 * -------------------------------------------------------------------- 33 */ 34 #ifdef HAVE_CONFIG_H 35 #include <config.h> 36 #endif 37 38 #include <string> 39 40 #include "depot.h" 41 #include "io.h" 42 #include "mailbox.h" 43 #include "operators.h" 44 #include "imapparser.h" 45 #include "pendingupdates.h" 46 #include "recursivedescent.h" 47 #include "session.h" 48 #include "convert.h" 49 50 using namespace ::std; 51 using namespace Binc; 52 53 namespace { 54 void outputFlags(const Message & message) 55 { 56 IO &com = IOFactory::getInstance().get(1); 57 58 com << "FLAGS "; 59 60 com << "("; 61 int flags = message.getStdFlags(); 62 vector<string> flagv; 63 if (flags & Message::F_SEEN) flagv.push_back("\\Seen"); 64 if (flags & Message::F_ANSWERED) flagv.push_back("\\Answered"); 65 if (flags & Message::F_DELETED) flagv.push_back("\\Deleted"); 66 if (flags & Message::F_DRAFT) flagv.push_back("\\Draft"); 67 if (flags & Message::F_RECENT) flagv.push_back("\\Recent"); 68 if (flags & Message::F_FLAGGED) flagv.push_back("\\Flagged"); 69 for (vector<string>::const_iterator k 70 = flagv.begin(); k != flagv.end(); ++k) { 71 if (k != flagv.begin()) com << " "; 72 com << *k; 73 } 74 com << ")"; 75 } 76 77 } 78 79 //---------------------------------------------------------------------- 80 FetchOperator::FetchOperator(void) 81 { 82 } 83 84 //---------------------------------------------------------------------- 85 FetchOperator::~FetchOperator(void) 86 { 87 } 88 89 //---------------------------------------------------------------------- 90 const string FetchOperator::getName(void) const 91 { 92 return "FETCH"; 93 } 94 95 //---------------------------------------------------------------------- 96 int FetchOperator::getState(void) const 97 { 98 return Session::SELECTED; 99 } 100 101 //------------------------------------------------------------------------ 102 Operator::ProcessResult FetchOperator::process(Depot &depot, 103 Request &request) 104 { 105 IO &com = IOFactory::getInstance().get(1); 106 Session &session = Session::getInstance(); 107 108 bool updateFlags = false; 109 Request req = request; 110 111 Mailbox *mailbox = depot.getSelected(); 112 113 // If this is a UID FETCH, check if the UID attribute is fetched. If 114 // it is not, then add it to the list of fetch attributes. 115 vector<BincImapParserFetchAtt>::const_iterator f_i; 116 bool uidfetched = false; 117 if (request.getUidMode()) { 118 f_i = request.fatt.begin(); 119 while (f_i != request.fatt.end()) { 120 if ((*f_i).type == "UID") { 121 uidfetched = true; 122 break; 123 } 124 125 f_i++; 126 } 127 128 if (!uidfetched) { 129 BincImapParserFetchAtt b; 130 b.type = "UID"; 131 req.fatt.push_back(b); 132 } 133 } 134 135 // Convert macros ALL, FULL and FAST 136 f_i = request.fatt.begin(); 137 while (f_i != request.fatt.end()) { 138 const string &type = (*f_i).type; 139 if (type == "ALL") { 140 req.fatt.push_back(BincImapParserFetchAtt("FLAGS")); 141 req.fatt.push_back(BincImapParserFetchAtt("INTERNALDATE")); 142 req.fatt.push_back(BincImapParserFetchAtt("RFC822.SIZE")); 143 req.fatt.push_back(BincImapParserFetchAtt("ENVELOPE")); 144 } else if (type == "FULL") { 145 req.fatt.push_back(BincImapParserFetchAtt("FLAGS")); 146 req.fatt.push_back(BincImapParserFetchAtt("INTERNALDATE")); 147 req.fatt.push_back(BincImapParserFetchAtt("RFC822.SIZE")); 148 req.fatt.push_back(BincImapParserFetchAtt("ENVELOPE")); 149 req.fatt.push_back(BincImapParserFetchAtt("BODY")); 150 } else if (type == "FAST") { 151 req.fatt.push_back(BincImapParserFetchAtt("FLAGS")); 152 req.fatt.push_back(BincImapParserFetchAtt("INTERNALDATE")); 153 req.fatt.push_back(BincImapParserFetchAtt("RFC822.SIZE")); 154 } 155 156 ++f_i; 157 } 158 159 int mode; 160 if (req.getUidMode()) 161 mode = Mailbox::UID_MODE; 162 else 163 mode = Mailbox::SQNR_MODE; 164 165 Mailbox::iterator i 166 = mailbox->begin(req.bset, Mailbox::SKIP_EXPUNGED | mode); 167 168 for (; i != mailbox->end(); ++i) { 169 Message &message = *i; 170 171 com << "* " << i.getSqnr() << " FETCH ("; 172 bool hasprinted = false; 173 f_i = req.fatt.begin(); 174 while (f_i != req.fatt.end()) { 175 BincImapParserFetchAtt fatt = *f_i; 176 177 string prefix = ""; 178 if (hasprinted) 179 prefix = " "; 180 181 if (fatt.type == "FLAGS") { 182 // FLAGS 183 hasprinted = true; 184 com << prefix; 185 186 outputFlags(message); 187 } else if (fatt.type == "UID") { 188 // UID 189 hasprinted = true; 190 com << prefix << "UID " << message.getUID(); 191 } else if (fatt.type == "RFC822.SIZE") { 192 // RFC822.SIZE 193 hasprinted = true; 194 com << prefix << "RFC822.SIZE " << message.getSize(true); 195 } else if (fatt.type == "ENVELOPE") { 196 // ENVELOPE 197 hasprinted = true; 198 com << prefix << "ENVELOPE "; 199 message.printEnvelope(); 200 } else if (fatt.type == "BODYSTRUCTURE") { 201 // BODYSTRUCTURE 202 hasprinted = true; 203 com << prefix << "BODYSTRUCTURE "; 204 message.printBodyStructure(true); 205 } else if (fatt.type == "BODY" && !fatt.hassection) { 206 // BODY with no section 207 hasprinted = true; 208 session.addBody(); 209 com << prefix << "BODY "; 210 message.printBodyStructure(false); 211 } else if (fatt.type == "INTERNALDATE") { 212 // INTERNALDATE 213 hasprinted = true; 214 com << prefix << "INTERNALDATE "; 215 216 time_t iDate = message.getInternalDate(); 217 struct tm *_tm = gmtime(&iDate); 218 char internal[64]; 219 string iDateStr; 220 if (strftime(internal, sizeof(internal), 221 "%d-%b-%Y %H:%M:%S %z", _tm) != 0) 222 iDateStr = internal; 223 else 224 iDateStr = "NIL"; 225 226 com << toImapString(iDateStr); 227 } else if (fatt.type == "BODY" || fatt.type == "BODY.PEEK") { 228 // BODY & BODY.PEEK 229 hasprinted = true; 230 session.addBody(); 231 232 com << prefix; 233 bool peek = (fatt.type == "BODY.PEEK"); 234 com << fatt.toString(); 235 236 bool includeheaders = true; 237 bool fullheader = false; 238 bool bodyfetch = false; 239 240 if (fatt.section != "" || fatt.sectiontext == "" 241 || fatt.sectiontext == "TEXT") { 242 bodyfetch = true; 243 fullheader = true; 244 } 245 246 if (fatt.sectiontext == "HEADER.FIELDS.NOT") 247 includeheaders = false; 248 249 if (fatt.sectiontext == "HEADER" 250 || fatt.sectiontext == "HEADER.FIELDS" 251 || fatt.sectiontext == "HEADER.FIELDS.NOT" 252 || fatt.sectiontext == "MIME") { 253 254 vector<string> v; 255 256 if (fatt.sectiontext == "MIME") { 257 v.push_back("content-type"); 258 v.push_back("content-transfer-encoding"); 259 v.push_back("content-disposition"); 260 v.push_back("content-description"); 261 } else 262 v = fatt.headerlist; 263 264 string dummy; 265 unsigned int size = fullheader 266 ? message.getHeaderSize(fatt.section, v, 267 true, 268 fatt.offsetstart, 269 fatt.offsetlength, fatt.sectiontext == "MIME") 270 : message.getHeaderSize(fatt.section, fatt.headerlist, 271 includeheaders, 272 fatt.offsetstart, 273 fatt.offsetlength, fatt.sectiontext == "MIME"); 274 275 com << "{" << size << "}\r\n"; 276 277 if (fullheader) { 278 message.printHeader(fatt.section, v, 279 true, 280 fatt.offsetstart, 281 fatt.offsetlength, fatt.sectiontext == "MIME"); 282 } else { 283 message.printHeader(fatt.section, fatt.headerlist, 284 includeheaders, 285 fatt.offsetstart, 286 fatt.offsetlength, fatt.sectiontext == "MIME"); 287 } 288 } else { 289 unsigned int size; 290 if ((fatt.sectiontext == "" || fatt.sectiontext == "TEXT") 291 && fatt.section == "") 292 size = message.getDocSize(fatt.offsetstart, 293 fatt.offsetlength, 294 fatt.sectiontext == "TEXT"); 295 else 296 size = message.getBodySize(fatt.section, 297 fatt.offsetstart, 298 fatt.offsetlength); 299 300 com << "{" << size << "}\r\n"; 301 302 if ((fatt.sectiontext == "" || fatt.sectiontext == "TEXT") 303 && fatt.section == "") 304 message.printDoc(fatt.offsetstart, 305 fatt.offsetlength, 306 fatt.sectiontext == "TEXT"); 307 else 308 message.printBody(fatt.section, fatt.offsetstart, 309 fatt.offsetlength); 310 } 311 312 // set the \Seen flag if .PEEK is not used. 313 if (!peek) 314 if ((message.getStdFlags() & Message::F_SEEN) == 0) 315 message.setStdFlag(Message::F_SEEN); 316 317 } else if (fatt.type == "RFC822") { 318 com << prefix; 319 hasprinted = true; 320 session.addBody(); 321 322 com << fatt.toString(); 323 324 unsigned int size = message.getDocSize(fatt.offsetstart, 325 fatt.offsetlength); 326 327 com << " {" << size << "}\r\n"; 328 329 message.printDoc(fatt.offsetstart, fatt.offsetlength); 330 331 // set the \Seen flag 332 if ((message.getStdFlags() & Message::F_SEEN) == 0) 333 message.setStdFlag(Message::F_SEEN); 334 335 } else if (fatt.type == "RFC822.HEADER") { 336 com << prefix; 337 hasprinted = true; 338 339 com << fatt.toString(); 340 341 vector<string> v; 342 string dummy; 343 344 unsigned int size = message.getHeaderSize("", v, true, 345 fatt.offsetstart, 346 fatt.offsetlength); 347 348 com << " {" << size << "}\r\n"; 349 350 message.printHeader("", v, true, fatt.offsetstart, 351 fatt.offsetlength); 352 353 } else if (fatt.type == "RFC822.TEXT") { 354 // RFC822.TEXT 355 com << prefix; 356 hasprinted = true; 357 session.addBody(); 358 359 com << fatt.toString(); 360 361 bool bodyfetch = false; 362 bodyfetch = true; 363 364 unsigned int size; 365 if (fatt.sectiontext == "" && fatt.section == "") 366 size = message.getDocSize(fatt.offsetstart, 367 fatt.offsetlength, true); 368 else 369 size = message.getBodySize(fatt.section, fatt.offsetstart, 370 fatt.offsetlength); 371 372 com << " {" << size << "}\r\n"; 373 374 if (fatt.sectiontext == "" && fatt.section == "") 375 message.printDoc(fatt.offsetstart, 376 fatt.offsetlength, true); 377 else 378 message.printBody(fatt.section, fatt.offsetstart, 379 fatt.offsetlength); 380 381 // set the \Seen flag 382 if ((message.getStdFlags() & Message::F_SEEN) == 0) 383 message.setStdFlag(Message::F_SEEN); 384 } else { 385 // Unrecognized fetch_att, this is stopped by the parser 386 // so we never get here. 387 } 388 389 f_i++; 390 } 391 392 // FIXME: how are parse error passed back? 393 394 com << ")" << endl; 395 396 if (message.hasFlagsChanged()) { 397 updateFlags = true; 398 com << "* " << i.getSqnr() << " FETCH ("; 399 outputFlags(message); 400 com << ")" << endl; 401 message.setFlagsUnchanged(); 402 } 403 } 404 405 if (updateFlags) mailbox->updateFlags(); 406 407 if (!pendingUpdates(mailbox, 408 PendingUpdates::FLAGS 409 | PendingUpdates::EXISTS 410 | PendingUpdates::RECENT 411 | PendingUpdates::EXPUNGE, 412 true)) { 413 IO &logger = IOFactory::getInstance().get(2); 414 logger << "when scanning mailbox: " 415 << session.getLastError() << endl; 416 com << "* BYE " << session.getLastError() << endl; 417 com.flushContent(); 418 session.setState(Session::LOGOUT); 419 return NOTHING; 420 } 421 422 return OK; 423 } 424 425 //---------------------------------------------------------------------- 426 Operator::ParseResult FetchOperator::parse(Request &c_in) const 427 { 428 Session &session = Session::getInstance(); 429 430 Operator::ParseResult res; 431 if ((res = expectSPACE()) != ACCEPT) { 432 session.setLastError("Expected SPACE after FETCH"); 433 return res; 434 } 435 436 if ((res = expectSet(c_in.getSet())) != ACCEPT) { 437 session.setLastError("Expected sequence set after FETCH SPACE"); 438 return res; 439 } 440 441 if ((res = expectSPACE()) != ACCEPT) { 442 session.setLastError("Expected SPACE after FETCH SPACE set"); 443 return res; 444 } 445 446 BincImapParserFetchAtt f; 447 448 if ((res = expectThisString("ALL")) == ACCEPT) { 449 f.type = "ALL"; 450 c_in.fatt.push_back(f); 451 } else if ((res = expectThisString("FULL")) == ACCEPT) { 452 f.type = "FULL"; 453 c_in.fatt.push_back(f); 454 } else if ((res = expectThisString("FAST")) == ACCEPT) { 455 f.type = "FAST"; 456 c_in.fatt.push_back(f); 457 } else if ((res = expectFetchAtt(f)) == ACCEPT) { 458 c_in.fatt.push_back(f); 459 } else if ((res = expectThisString("(")) == ACCEPT) { 460 while (1) { 461 BincImapParserFetchAtt ftmp; 462 if ((res = expectFetchAtt(ftmp)) != ACCEPT) { 463 session.setLastError("Expected fetch_att"); 464 return res; 465 } 466 467 c_in.fatt.push_back(ftmp); 468 469 if ((res = expectSPACE()) == REJECT) 470 break; 471 else if (res == ERROR) 472 return ERROR; 473 } 474 475 if ((res = expectThisString(")")) != ACCEPT) { 476 session.setLastError("Expected )"); 477 return res; 478 } 479 } else { 480 session.setLastError("Expected ALL, FULL, FAST, fetch_att or ("); 481 return res; 482 } 483 484 if ((res = expectCRLF()) != ACCEPT) { 485 session.setLastError("Expected CRLF"); 486 return res; 487 } 488 489 c_in.setName("FETCH"); 490 return ACCEPT; 491 } 492 493 //---------------------------------------------------------------------- 494 Operator::ParseResult 495 FetchOperator::expectSectionText(BincImapParserFetchAtt &f_in) const 496 { 497 Session &session = Session::getInstance(); 498 499 Operator::ParseResult res; 500 if ((res = expectThisString("HEADER")) == ACCEPT) { 501 f_in.sectiontext = "HEADER"; 502 503 if ((res = expectThisString(".FIELDS")) == ACCEPT) { 504 f_in.sectiontext += ".FIELDS"; 505 506 if ((res = expectThisString(".NOT")) == ACCEPT) 507 f_in.sectiontext += ".NOT"; 508 509 if ((res = expectSPACE()) != ACCEPT) { 510 session.setLastError("expected SPACE"); 511 return res; 512 } 513 514 if ((res = expectHeaderList(f_in)) != ACCEPT) { 515 session.setLastError("Expected header_list"); 516 return res; 517 } 518 } 519 } else if ((res = expectThisString("TEXT")) == ACCEPT) 520 f_in.sectiontext = "TEXT"; 521 else 522 return REJECT; 523 524 return ACCEPT; 525 526 } 527 528 //---------------------------------------------------------------------- 529 Operator::ParseResult 530 FetchOperator::expectSection(BincImapParserFetchAtt &f_in) const 531 { 532 Session &session = Session::getInstance(); 533 534 Operator::ParseResult res; 535 if ((res = expectThisString("[")) != ACCEPT) 536 return REJECT; 537 538 if ((res = expectSectionText(f_in)) != ACCEPT) { 539 unsigned int n; 540 if ((res = expectNZNumber(n)) == ACCEPT) { 541 542 BincStream nstr; 543 544 nstr << n; 545 546 f_in.section = nstr.str(); 547 548 bool gotadotalready = false; 549 while (1) { 550 if ((res = expectThisString(".")) != ACCEPT) 551 break; 552 553 if ((res = expectNZNumber(n)) != ACCEPT) { 554 gotadotalready = true; 555 break; 556 } 557 558 f_in.section += "."; 559 560 BincStream nstr; 561 562 nstr << n; 563 564 f_in.section += nstr.str(); 565 } 566 567 if (gotadotalready || (res = expectThisString(".")) == ACCEPT) { 568 if ((res = expectThisString("MIME")) == ACCEPT) { 569 f_in.sectiontext = "MIME"; 570 } else if ((res = expectSectionText(f_in)) != ACCEPT) { 571 session.setLastError("Expected MIME or section_text"); 572 return res; 573 } 574 } 575 } 576 } 577 578 if ((res = expectThisString("]")) != ACCEPT) { 579 session.setLastError("Expected ]"); 580 return res; 581 } 582 583 return ACCEPT; 584 } 585 586 //---------------------------------------------------------------------- 587 Operator::ParseResult 588 FetchOperator::expectHeaderList(BincImapParserFetchAtt &f_in) const 589 { 590 Session &session = Session::getInstance(); 591 592 Operator::ParseResult res; 593 if ((res = expectThisString("(")) != ACCEPT) 594 return REJECT; 595 596 string header_fld_name; 597 while (1) { 598 if ((res = expectAstring(header_fld_name)) != ACCEPT) { 599 session.setLastError("Expected header_fld_name"); 600 return res; 601 } 602 603 f_in.headerlist.push_back(header_fld_name); 604 605 if ((res = expectSPACE()) == ACCEPT) 606 continue; 607 else break; 608 } 609 610 if ((res = expectThisString(")")) != ACCEPT) { 611 session.setLastError("Expected )"); 612 return res; 613 } 614 615 return ACCEPT; 616 } 617 618 //---------------------------------------------------------------------- 619 Operator::ParseResult 620 FetchOperator::expectOffset(BincImapParserFetchAtt &f_in) const 621 { 622 Session &session = Session::getInstance(); 623 Operator::ParseResult res; 624 625 if ((res = expectThisString("<")) != ACCEPT) 626 return REJECT; 627 628 unsigned int i; 629 if ((res = expectNumber(i)) != ACCEPT) { 630 session.setLastError("Expected number"); 631 return res; 632 } 633 634 if ((res = expectThisString(".")) != ACCEPT) { 635 session.setLastError("Expected ."); 636 return res; 637 } 638 639 unsigned int j; 640 if ((res = expectNZNumber(j)) != ACCEPT) { 641 session.setLastError("expected nz_number"); 642 return res; 643 } 644 645 if ((res = expectThisString(">")) != ACCEPT) { 646 session.setLastError("Expected >"); 647 return res; 648 } 649 650 f_in.offsetstart = i; 651 f_in.offsetlength = j; 652 return ACCEPT; 653 } 654 655 //---------------------------------------------------------------------- 656 Operator::ParseResult 657 FetchOperator::expectFetchAtt(BincImapParserFetchAtt &f_in) const 658 { 659 Operator::ParseResult res; 660 661 Session &session = Session::getInstance(); 662 663 if ((res = expectThisString("ENVELOPE")) == ACCEPT) f_in.type = "ENVELOPE"; 664 else if ((res = expectThisString("FLAGS")) == ACCEPT) f_in.type = "FLAGS"; 665 else if ((res = expectThisString("INTERNALDATE")) == ACCEPT) 666 f_in.type = "INTERNALDATE"; 667 else if ((res = expectThisString("UID")) == ACCEPT) f_in.type = "UID"; 668 else if ((res = expectThisString("RFC822")) == ACCEPT) { 669 f_in.type = "RFC822"; 670 if ((res = expectThisString(".HEADER")) == ACCEPT) f_in.type += ".HEADER"; 671 else if ((res = expectThisString(".SIZE")) == ACCEPT) f_in.type += ".SIZE"; 672 else if ((res = expectThisString(".TEXT")) == ACCEPT) f_in.type += ".TEXT"; 673 else if ((res = expectThisString(".")) == ACCEPT) { 674 session.setLastError("Expected RFC822, RFC822.HEADER," 675 " RFC822.SIZE or RFC822.TEXT"); 676 return ERROR; 677 } 678 679 } else if ((res = expectThisString("BODY")) == ACCEPT) { 680 f_in.type = "BODY"; 681 682 if ((res = expectThisString("STRUCTURE")) == ACCEPT) f_in.type += "STRUCTURE"; 683 else if ((res = expectThisString(".PEEK")) == ACCEPT) f_in.type += ".PEEK"; 684 685 if ((res = expectSection(f_in)) != ACCEPT) 686 f_in.hassection = false; 687 else { 688 f_in.hassection = true; 689 690 if ((res = expectOffset(f_in)) == ERROR) 691 return ERROR; 692 } 693 } else 694 return REJECT; 695 696 return ACCEPT; 697 }