bincimap

Log | Files | Refs | LICENSE

operator-search.cc (29688B)


      1 /* -*- Mode: c++; -*- */
      2 /*  --------------------------------------------------------------------
      3  *  Filename:
      4  *    operator-search.cc
      5  *  
      6  *  Description:
      7  *    Implementation of the SEARCH 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 #include <iostream>
     40 #include <algorithm>
     41 
     42 #include <ctype.h>
     43     
     44 #include "imapparser.h"
     45 #include "mailbox.h"
     46 #include "mime.h"
     47 #include "io.h"
     48 #include "convert.h"
     49 
     50 #include "recursivedescent.h"
     51 
     52 #include "session.h"
     53 #include "depot.h"
     54 #include "operators.h"
     55 
     56 using namespace ::std;
     57 using namespace Binc;
     58 
     59 //----------------------------------------------------------------------
     60 bool SearchOperator::SearchNode::convertDate(const string &date, 
     61 					     time_t &t,
     62 					     const string &delim)
     63 {
     64   vector<string> parts;
     65   split(date, delim, parts);
     66   if (parts.size() < 3) return false;
     67   
     68   struct tm mold;
     69   memset((char *) &mold, 0, sizeof(struct tm));
     70   mold.tm_mday = atoi(parts[0].c_str());
     71   mold.tm_year = atoi(parts[2].c_str()) - 1900;
     72   
     73   // accept mixed case months. this is more than the standard
     74   // accepts.
     75   string month = parts[1];
     76   lowercase(month);
     77   
     78   if (month == "jan") mold.tm_mon = 0;
     79   else if (month == "feb") mold.tm_mon = 1;
     80   else if (month == "mar") mold.tm_mon = 2;
     81   else if (month == "apr") mold.tm_mon = 3;
     82   else if (month == "may") mold.tm_mon = 4;
     83   else if (month == "jun") mold.tm_mon = 5;
     84   else if (month == "jul") mold.tm_mon = 6;
     85   else if (month == "aug") mold.tm_mon = 7;
     86   else if (month == "sep") mold.tm_mon = 8;
     87   else if (month == "oct") mold.tm_mon = 9;
     88   else if (month == "nov") mold.tm_mon = 10;
     89   else if (month == "dec") mold.tm_mon = 11;
     90   
     91   t = mktime(&mold);
     92   return true;
     93 }
     94 
     95 //----------------------------------------------------------------------
     96 bool SearchOperator::SearchNode::convertDateHeader(const string &d_in,
     97 						   time_t &t)
     98 {
     99   string date = d_in;
    100   string::size_type n = date.find(',');
    101   if (n != string::npos)
    102     date = date.substr(n + 1);
    103   trim(date);
    104   
    105   bool result = convertDate(date, t, " ");
    106   return result;
    107 }
    108 
    109 //----------------------------------------------------------------------
    110 SearchOperator::SearchNode::SearchNode(void)
    111 {
    112 }
    113 
    114 //----------------------------------------------------------------------
    115 SearchOperator::SearchNode::SearchNode(const BincImapParserSearchKey &a)
    116 {
    117   init(a);
    118 }
    119 
    120 //----------------------------------------------------------------------
    121 int SearchOperator::SearchNode::getType(void) const
    122 {
    123   return type;
    124 }
    125 
    126 //----------------------------------------------------------------------
    127 bool SearchOperator::SearchNode::match(Mailbox *mailbox,
    128 				       Message *m,
    129 				       unsigned int seqnr,
    130 				       unsigned int lastmessage,
    131 				       unsigned int lastuid) const
    132 {
    133   HeaderItem hitem;
    134   string tmp;
    135 
    136   switch (type) {
    137     //--------------------------------------------------------------------
    138   case S_ALL: 
    139     return true;
    140     //--------------------------------------------------------------------
    141   case S_ANSWERED: 
    142     return (m->getStdFlags() & Message::F_ANSWERED);
    143     //--------------------------------------------------------------------
    144   case S_BCC:
    145     return m->headerContains("bcc", astring);
    146     //--------------------------------------------------------------------
    147   case S_BEFORE: {
    148     time_t mtime = m->getInternalDate();
    149     struct tm *mtime_ = localtime(&mtime);
    150     mtime_->tm_sec = 0;
    151     mtime_->tm_min = 0;
    152     mtime_->tm_hour = 0;
    153     mtime_->tm_wday = 0;
    154     mtime_->tm_yday = 0;
    155     mtime_->tm_isdst = 0;
    156     mtime = mktime(mtime_);
    157 
    158     time_t atime;
    159     if (!convertDate(date, atime)) {
    160       IO &logger = IOFactory::getInstance().get(2);
    161 
    162       logger << "warning, unable to convert " << date << 
    163 	" to a time_t" << endl;
    164       return false;
    165     }
    166 
    167     return mtime < atime;
    168   } //--------------------------------------------------------------------
    169   case S_BODY:
    170     return m->bodyContains(astring);
    171     //--------------------------------------------------------------------
    172   case S_CC:
    173     return m->headerContains("cc", astring);
    174     //--------------------------------------------------------------------
    175   case S_DELETED:
    176     return (m->getStdFlags() & Message::F_DELETED);
    177     //--------------------------------------------------------------------
    178   case S_FLAGGED:
    179     return (m->getStdFlags() & Message::F_FLAGGED);
    180     //--------------------------------------------------------------------
    181   case S_FROM:
    182     return m->headerContains("from", astring);
    183     //--------------------------------------------------------------------
    184   case S_KEYWORD: 
    185     // the server does not support keywords
    186     return false;
    187     //--------------------------------------------------------------------
    188   case S_NEW:
    189     return (m->getStdFlags() & Message::F_RECENT) 
    190       && !(m->getStdFlags() & Message::F_SEEN);
    191     //--------------------------------------------------------------------
    192   case S_OLD:
    193     return !(m->getStdFlags() & Message::F_RECENT);
    194     //--------------------------------------------------------------------
    195   case S_ON: {
    196     time_t mtime = m->getInternalDate();
    197     struct tm *mtime_ = localtime(&mtime);
    198     mtime_->tm_sec = 0;
    199     mtime_->tm_min = 0;
    200     mtime_->tm_hour = 0;
    201     mtime_->tm_wday = 0;
    202     mtime_->tm_yday = 0;
    203     mtime_->tm_isdst = 0;
    204     mtime = mktime(mtime_);
    205 
    206     time_t atime;
    207     if (!convertDate(date, atime)) {
    208       IO &logger = IOFactory::getInstance().get(2);
    209 
    210       logger << "warning, unable to convert " << date << 
    211 	" to a time_t" << endl;
    212       return false;
    213     }
    214 
    215     return mtime == atime;
    216   } //--------------------------------------------------------------------
    217   case S_RECENT:
    218     return (m->getStdFlags() & Message::F_RECENT);
    219     //--------------------------------------------------------------------
    220   case S_SEEN:
    221     return (m->getStdFlags() & Message::F_SEEN);
    222     //--------------------------------------------------------------------
    223   case S_SINCE: {
    224     time_t mtime = m->getInternalDate();
    225     struct tm *mtime_ = localtime(&mtime);
    226     mtime_->tm_sec = 0;
    227     mtime_->tm_min = 0;
    228     mtime_->tm_hour = 0;
    229     mtime_->tm_wday = 0;
    230     mtime_->tm_yday = 0;
    231     mtime_->tm_isdst = 0;
    232     mtime = mktime(mtime_);
    233 
    234     time_t atime;
    235     if (!convertDate(date, atime)) {
    236       IO &logger = IOFactory::getInstance().get(2);
    237 
    238       logger << "warning, unable to convert " << date << 
    239 	" to a time_t" << endl;
    240       return false;
    241     }
    242 
    243     return mtime >= atime;
    244   } //--------------------------------------------------------------------
    245   case S_SUBJECT:
    246     return m->headerContains("subject", astring);
    247     //--------------------------------------------------------------------
    248   case S_TEXT:
    249     return m->textContains(astring);
    250     //--------------------------------------------------------------------
    251   case S_TO:
    252     return m->headerContains("to", astring);
    253     //--------------------------------------------------------------------
    254   case S_UNANSWERED:
    255     return !(m->getStdFlags() & Message::F_ANSWERED);
    256     //--------------------------------------------------------------------
    257   case S_UNDELETED:
    258     return !(m->getStdFlags() & Message::F_DELETED);
    259     //--------------------------------------------------------------------
    260   case S_UNFLAGGED:
    261     return !(m->getStdFlags() & Message::F_FLAGGED);
    262     //--------------------------------------------------------------------
    263   case S_UNKEYWORD:
    264     // the server does not support keywords
    265     return true;
    266     //--------------------------------------------------------------------
    267   case S_UNSEEN:
    268     return !(m->getStdFlags() & Message::F_SEEN);
    269     //--------------------------------------------------------------------
    270   case S_DRAFT:
    271     return (m->getStdFlags() & Message::F_DRAFT);
    272     //--------------------------------------------------------------------
    273   case S_HEADER:
    274     return m->headerContains(astring, bstring);
    275     //--------------------------------------------------------------------
    276   case S_LARGER: {
    277     return (m->getSize(true) > number);
    278   }
    279     //--------------------------------------------------------------------
    280   case S_NOT:
    281     for (vector<SearchNode>::const_iterator i = children.begin();
    282 	 i != children.end(); ++i)
    283       if ((*i).match(mailbox, m, seqnr, lastmessage, lastuid))
    284 	return false;
    285     return true;
    286     //--------------------------------------------------------------------
    287   case S_OR:
    288     for (vector<SearchNode>::const_iterator i = children.begin();
    289 	 i != children.end(); ++i)
    290       if ((*i).match(mailbox, m, seqnr, lastmessage, lastuid))
    291 	return true;
    292     return false;
    293     //--------------------------------------------------------------------
    294   case S_SENTBEFORE: {
    295     string tmp = m->getHeader("date");
    296     if (tmp == "")
    297       return false;
    298 
    299     lowercase(tmp);
    300 
    301     time_t mtime;
    302     if (!convertDateHeader(tmp, mtime))
    303       return false;
    304 
    305     if (mtime == (time_t) -1)
    306       return false;
    307 
    308     time_t atime;
    309     if (!convertDate(date, atime)) {
    310       IO &logger = IOFactory::getInstance().get(2);
    311 
    312       logger << "warning, unable to convert " << date << 
    313 	" to a time_t" << endl;
    314       return false;
    315     }
    316 
    317     return mtime < atime;
    318   } //--------------------------------------------------------------------
    319   case S_SENTON: {
    320     string tmp = m->getHeader("date");
    321     if (tmp == "")
    322       return false;
    323 
    324     lowercase(tmp);
    325 
    326     time_t mtime;
    327     if (!convertDateHeader(tmp, mtime))
    328       return false;
    329 
    330     if (mtime == (time_t) -1)
    331       return false;
    332 
    333     time_t atime;
    334     if (!convertDate(date, atime)) {
    335       IO &logger = IOFactory::getInstance().get(2);
    336 
    337       logger << "warning, unable to convert " << date << 
    338 	" to a time_t" << endl;
    339       return false;
    340     }
    341 
    342     return mtime == atime;
    343   } //--------------------------------------------------------------------
    344   case S_SENTSINCE: {
    345     string tmp = m->getHeader("date");
    346     if (tmp == "")
    347       return false;
    348 
    349     lowercase(tmp);
    350 
    351     time_t mtime;
    352     if (!convertDateHeader(tmp, mtime))
    353       return false;
    354 
    355     if (mtime == (time_t) -1)
    356       return false;
    357 
    358     time_t atime;
    359     if (!convertDate(date, atime)) {
    360       IO &logger = IOFactory::getInstance().get(2);
    361 
    362       logger << "warning, unable to convert " << date << 
    363 	" to a time_t" << endl;
    364       return false;
    365     }
    366 
    367     return mtime >= atime;
    368   } //--------------------------------------------------------------------
    369   case S_SMALLER:
    370     return (m->getSize(true) < number);
    371     //--------------------------------------------------------------------
    372   case S_UID:
    373     if (!bset->isInSet(m->getUID()))
    374       if (!(m->getUID() == lastuid && !bset->isLimited()))
    375 	return false;
    376     return true;
    377     //--------------------------------------------------------------------
    378   case S_UNDRAFT:
    379     return !(m->getStdFlags() & Message::F_DRAFT);
    380     //--------------------------------------------------------------------
    381   case S_SET:
    382     if (!bset->isInSet(seqnr))
    383       if (!(seqnr == lastmessage && !bset->isLimited()))
    384 	return false;
    385     return true;
    386     //--------------------------------------------------------------------
    387   case S_AND:
    388     for (vector<SearchNode>::const_iterator i = children.begin();
    389 	 i != children.end(); ++i)
    390       if (!(*i).match(mailbox, m, seqnr, lastmessage, lastuid))
    391 	return false;
    392     return true;
    393   }
    394 
    395   return false;
    396 }
    397 
    398 //----------------------------------------------------------------------
    399 void SearchOperator::SearchNode::init(const BincImapParserSearchKey &a)
    400 {
    401   astring = a.astring;
    402   bstring = a.bstring;
    403   date = a.date;
    404   number = a.number;
    405   uppercase(astring);
    406   uppercase(bstring);
    407   uppercase(date);
    408 
    409   if (a.name      == "ALL")            { type = S_ALL;        weight = 1; }
    410   else if (a.name == "ANSWERED")       { type = S_ANSWERED;   weight = 1; }
    411   else if (a.name == "BCC")            { type = S_BCC;        weight = 2; }
    412   else if (a.name == "BEFORE")         { type = S_BEFORE;     weight = 2; }
    413   else if (a.name == "BODY")           { type = S_BODY;       weight = 1; }
    414   else if (a.name == "CC")             { type = S_CC;         weight = 2; }
    415   else if (a.name == "DELETED")        { type = S_DELETED;    weight = 1; }
    416   else if (a.name == "FLAGGED")        { type = S_FLAGGED;    weight = 1; }
    417   else if (a.name == "FROM")           { type = S_FROM;       weight = 2; }
    418   else if (a.name == "KEYWORD")        { type = S_KEYWORD;    weight = 3; }
    419   else if (a.name == "NEW")            { type = S_NEW;        weight = 1; }
    420   else if (a.name == "OLD")            { type = S_OLD;        weight = 1; }
    421   else if (a.name == "ON")             { type = S_ON;         weight = 1; }
    422   else if (a.name == "RECENT")         { type = S_RECENT;     weight = 1; }
    423   else if (a.name == "SEEN")           { type = S_SEEN;       weight = 1; }
    424   else if (a.name == "SINCE")          { type = S_SINCE;      weight = 1; }
    425   else if (a.name == "SUBJECT")        { type = S_SUBJECT;    weight = 2; }
    426   else if (a.name == "TEXT")           { type = S_TEXT;       weight = 4; }
    427   else if (a.name == "TO")             { type = S_TO;         weight = 2; }
    428   else if (a.name == "UNANSWERED")     { type = S_UNANSWERED; weight = 1; }
    429   else if (a.name == "UNDELETED")      { type = S_UNDELETED;  weight = 1; }
    430   else if (a.name == "UNFLAGGED")      { type = S_UNFLAGGED;  weight = 1; }
    431   else if (a.name == "UNKEYWORD")      { type = S_UNKEYWORD;  weight = 1; }
    432   else if (a.name == "UNSEEN")         { type = S_UNSEEN;     weight = 1; }
    433   else if (a.name == "DRAFT")          { type = S_DRAFT;      weight = 1; }
    434   else if (a.name == "HEADER")         { type = S_HEADER;     weight = 3; }
    435   else if (a.name == "LARGER")         { type = S_LARGER;     weight = 4; }
    436   else if (a.name == "NOT")            {
    437     // *******                         NOT
    438     type = S_NOT;
    439     weight = 1;
    440 
    441     vector<BincImapParserSearchKey>::const_iterator i = a.children.begin();
    442     while (i != a.children.end()) {
    443       SearchNode b(*i);
    444       weight += b.getWeight();
    445       children.push_back(b);
    446       ++i;
    447     }
    448 
    449   } else if (a.name == "OR") {
    450     // *******                         OR
    451     type = S_OR;
    452     weight = 0;
    453 
    454     vector<BincImapParserSearchKey>::const_iterator i = a.children.begin();
    455     while (i != a.children.end()) {
    456       SearchNode b(*i);
    457       weight += b.getWeight();
    458 
    459       children.push_back(b);
    460       ++i;
    461     }
    462 
    463   } else if (a.name == "SENTBEFORE")   { type = S_SENTBEFORE; weight = 1; }
    464   else if (a.name == "SENTON")         { type = S_SENTON;     weight = 1; }
    465   else if (a.name == "SENTSINCE")      { type = S_SENTSINCE;  weight = 1; }
    466   else if (a.name == "SMALLER")        { type = S_SMALLER;    weight = 4; } 
    467   else if (a.name == "UID") {
    468     bset = &a.getSet();
    469     type = S_UID;
    470     weight = 1;
    471   } else if (a.name == "UNDRAFT")        { type = S_UNDRAFT;    weight = 1; }
    472   else if (a.type == BincImapParserSearchKey::KEY_SET) {
    473     bset = &a.getSet();
    474     type = S_SET;
    475     weight = 1;
    476   } else if (a.type == BincImapParserSearchKey::KEY_AND) {
    477     // *******                         AND
    478     type = S_AND;
    479     weight = 0;
    480 
    481     vector<BincImapParserSearchKey>::const_iterator i = a.children.begin();
    482     while (i != a.children.end()) {
    483       SearchNode b(*i);
    484       weight += b.getWeight();
    485       children.push_back(b);
    486       ++i;
    487     }
    488   }
    489 }
    490 
    491 //----------------------------------------------------------------------
    492 int SearchOperator::SearchNode::getWeight(void) const
    493 {
    494   return weight;
    495 }
    496 
    497 //----------------------------------------------------------------------
    498 void SearchOperator::SearchNode::setWeight(int i)
    499 {
    500   weight = i;
    501 }
    502 
    503 //----------------------------------------------------------------------
    504 void SearchOperator::SearchNode::order(void)
    505 {
    506   for (vector<SearchNode>::iterator i = children.begin();
    507        i != children.end(); ++i)
    508     (*i).order();
    509   ::stable_sort(children.begin(), children.end(), compareNodes);
    510 }
    511 
    512 //----------------------------------------------------------------------
    513 SearchOperator::SearchOperator(void)
    514 {
    515 }
    516 
    517 //----------------------------------------------------------------------
    518 SearchOperator::~SearchOperator(void)
    519 {
    520 }
    521 
    522 //----------------------------------------------------------------------
    523 const string SearchOperator::getName(void) const
    524 {
    525   return "SEARCH";
    526 }
    527 
    528 //----------------------------------------------------------------------
    529 int SearchOperator::getState(void) const
    530 {
    531   return Session::SELECTED;
    532 }
    533 
    534 //------------------------------------------------------------------------
    535 Operator::ProcessResult SearchOperator::process(Depot &depot,
    536 						Request &command)
    537 {
    538   IO &com = IOFactory::getInstance().get(1);
    539   Session &session = Session::getInstance();
    540 
    541   Mailbox *mailbox = depot.getSelected();
    542 
    543   if (command.getCharSet() != "" && command.getCharSet() != "US-ASCII") {
    544     session.setResponseCode("BADCHARSET (\"US-ASCII\")");
    545     return NO;
    546   }
    547 
    548   com << "* SEARCH";
    549 
    550   SearchNode s(command.searchkey);
    551   s.order();
    552 
    553   const unsigned int maxsqnr = mailbox->getMaxSqnr();
    554   const unsigned int maxuid = mailbox->getMaxUid();
    555 
    556   Mailbox::iterator i
    557     = mailbox->begin(SequenceSet::all(), Mailbox::SKIP_EXPUNGED);
    558   for (; i != mailbox->end(); ++i) {
    559     Message &message = *i;
    560 
    561     if (s.match(mailbox, &message, i.getSqnr(), maxsqnr, maxuid)) {
    562       com << " " << (command.getUidMode() ?  message.getUID() : i.getSqnr());
    563       com.flushContent();
    564     }
    565 
    566     message.close();
    567   }
    568 
    569   com << endl;
    570   return OK;
    571 }
    572 
    573 //------------------------------------------------------------------------
    574 Operator::ParseResult SearchOperator::parse(Request & c_in) const
    575 {
    576   Session &session = Session::getInstance();
    577 
    578   Operator::ParseResult res;
    579   if ((res = expectSPACE()) != ACCEPT) {
    580     session.setLastError("Expected SPACE");
    581     return res;
    582   }
    583 
    584   if ((res = expectThisString("CHARSET")) == ACCEPT) {
    585     if ((res = expectSPACE()) != ACCEPT) {
    586       session.setLastError("Expected SPACE after CHARSET");
    587       return res;
    588     }
    589 
    590     string charset;
    591     if ((res = expectAstring(charset)) != ACCEPT) {
    592       session.setLastError("Expected astring after CHARSET SPACE");
    593       return res;
    594     }
    595 
    596     c_in.setCharSet(charset);
    597     
    598     if ((res = expectSPACE()) != ACCEPT) {
    599       session.setLastError("Expected SPACE after CHARSET SPACE astring");
    600       return res;
    601     }
    602   }
    603 
    604   BincImapParserSearchKey b;
    605   if ((res = expectSearchKey(b)) != ACCEPT) {
    606     session.setLastError("Expected search_key");
    607     return res;
    608   }
    609 
    610   c_in.searchkey.type = BincImapParserSearchKey::KEY_AND;
    611   c_in.searchkey.children.push_back(b);
    612   
    613   while (1) {
    614     if ((res = expectSPACE()) != ACCEPT)
    615       break;
    616 
    617     BincImapParserSearchKey c;
    618     if ((res = expectSearchKey(c)) != ACCEPT) {
    619       session.setLastError("Expected search_key after search_key SPACE");
    620       return res;
    621     }
    622       
    623     c_in.searchkey.children.push_back(c);
    624   }
    625 
    626   if ((res = expectCRLF()) != ACCEPT) {
    627     session.setLastError("Expected CRLF after search_key");
    628     return res;
    629   }
    630   
    631   c_in.setName("SEARCH");
    632   return ACCEPT;
    633 }
    634 
    635 //----------------------------------------------------------------------
    636 Operator::ParseResult
    637 SearchOperator::expectSearchKey(BincImapParserSearchKey &s_in) const
    638 {
    639   Session &session = Session::getInstance();
    640   Operator::ParseResult res;
    641 
    642   s_in.type = BincImapParserSearchKey::KEY_OTHER;
    643   if ((res = expectThisString("ALL")) == ACCEPT) s_in.name = "ALL";
    644   else if ((res = expectThisString("ANSWERED")) == ACCEPT) s_in.name = "ANSWERED";
    645   else if ((res = expectThisString("BCC")) == ACCEPT) {
    646     s_in.name = "BCC";
    647     if ((res = expectSPACE()) != ACCEPT) {
    648       session.setLastError("Expected SPACE");
    649       return res;
    650     }
    651 
    652     if ((res = expectAstring(s_in.astring)) != ACCEPT) {
    653       session.setLastError("Expected astring");
    654       return res;
    655     }
    656   } else if ((res = expectThisString("BEFORE")) == ACCEPT) {
    657     s_in.name = "BEFORE";
    658       
    659     if ((res = expectSPACE()) != ACCEPT) {
    660       session.setLastError("Expected SPACE");
    661       return res;
    662     }
    663 
    664     if ((res = expectDate(s_in.date)) != ACCEPT) {
    665       session.setLastError("Expected date");
    666       return res;
    667     }
    668   } else if ((res = expectThisString("BODY")) == ACCEPT) {
    669     s_in.name = "BODY";
    670     if ((res = expectSPACE()) != ACCEPT) {
    671       session.setLastError("Expected SPACE");
    672       return res;
    673     }
    674 
    675     if ((res = expectAstring(s_in.astring)) != ACCEPT) {
    676       session.setLastError("Expected astring");
    677       return res;
    678     }
    679   } else if ((res = expectThisString("CC")) == ACCEPT) {
    680     s_in.name = "CC";
    681     if ((res = expectSPACE()) != ACCEPT) {
    682       session.setLastError("Expected SPACE");
    683       return res;
    684     }
    685 
    686     if ((res = expectAstring(s_in.astring)) != ACCEPT) {
    687       session.setLastError("Expected astring");
    688       return res;
    689     }
    690   } else if ((res = expectThisString("DELETED")) == ACCEPT) s_in.name = "DELETED";
    691   else if ((res = expectThisString("FLAGGED")) == ACCEPT) s_in.name = "FLAGGED";
    692   else if ((res = expectThisString("FROM")) == ACCEPT) {
    693     s_in.name = "FROM";
    694     if ((res = expectSPACE()) != ACCEPT) {
    695       session.setLastError("Expected SPACE");
    696       return res;
    697     }
    698 
    699     if ((res = expectAstring(s_in.astring)) != ACCEPT) {
    700       session.setLastError("Expected astring");
    701       return res;
    702     }
    703   } else if ((res = expectThisString("KEYWORD")) == ACCEPT) {
    704     s_in.name = "KEYWORD";
    705     if ((res = expectSPACE()) != ACCEPT) {
    706       session.setLastError("Expected SPACE");
    707       return res;
    708     }
    709 
    710     if ((res = expectAtom(s_in.astring)) != ACCEPT) {
    711       session.setLastError("Expected flag_keyword");
    712       return res;
    713     }
    714   } else if ((res = expectThisString("NEW")) == ACCEPT) s_in.name = "NEW";
    715   else if ((res = expectThisString("OLD")) == ACCEPT) s_in.name = "OLD";
    716   else if ((res = expectThisString("ON")) == ACCEPT) {
    717     s_in.name = "ON";
    718       
    719     if ((res = expectSPACE()) != ACCEPT) {
    720       session.setLastError("Expected SPACE");
    721       return res;
    722     }
    723 
    724     if ((res = expectDate(s_in.date)) != ACCEPT) {
    725       session.setLastError("Expected date");
    726       return res;
    727     }
    728   } else if ((res = expectThisString("RECENT")) == ACCEPT) s_in.name = "RECENT";
    729   else if ((res = expectThisString("SEEN")) == ACCEPT) s_in.name = "SEEN";
    730   else if ((res = expectThisString("SINCE")) == ACCEPT) {
    731     s_in.name = "SINCE";
    732       
    733     if ((res = expectSPACE()) != ACCEPT) {
    734       session.setLastError("Expected SPACE");
    735       return res;
    736     }
    737 
    738     if ((res = expectDate(s_in.date)) != ACCEPT) {
    739       session.setLastError("Expected date");   
    740       return res;
    741     }
    742   } else if ((res = expectThisString("SUBJECT")) == ACCEPT) {
    743     s_in.name = "SUBJECT";
    744     if ((res = expectSPACE()) != ACCEPT) {
    745       session.setLastError("Expected SPACE");
    746       return res;
    747     }
    748 
    749     if ((res = expectAstring(s_in.astring)) != ACCEPT) {
    750       session.setLastError("Expected astring");
    751       return res;
    752     }
    753   } else if ((res = expectThisString("TEXT")) == ACCEPT) {
    754     s_in.name = "TEXT";
    755     if ((res = expectSPACE()) != ACCEPT) {
    756       session.setLastError("Expected SPACE");
    757       return res;
    758     }
    759 
    760     if ((res = expectAstring(s_in.astring)) != ACCEPT) {
    761       session.setLastError("Expected astring");
    762       return res;
    763     }
    764   } else if ((res = expectThisString("TO")) == ACCEPT) {
    765     s_in.name = "TO";
    766     if ((res = expectSPACE()) != ACCEPT) {
    767       session.setLastError("Expected SPACE");
    768       return res;
    769     }
    770 
    771     if ((res = expectAstring(s_in.astring)) != ACCEPT) {
    772       session.setLastError("Expected astring");
    773       return res;
    774     }
    775   } else if ((res = expectThisString("UNANSWERED")) == ACCEPT)
    776     s_in.name = "UNANSWERED";
    777   else if ((res = expectThisString("UNDELETED")) == ACCEPT) s_in.name = "UNDELETED";
    778   else if ((res = expectThisString("UNFLAGGED")) == ACCEPT) s_in.name = "UNFLAGGED";
    779   else if ((res = expectThisString("UNKEYWORD")) == ACCEPT) {
    780     s_in.name = "UNKEYWORD";
    781     if ((res = expectSPACE()) != ACCEPT) {
    782       session.setLastError("Expected SPACE");
    783       return res;
    784     }
    785 
    786     if ((res = expectAtom(s_in.astring)) != ACCEPT) {
    787       session.setLastError("Expected flag_keyword");
    788       return res;
    789     }
    790   } else if ((res = expectThisString("UNSEEN")) == ACCEPT) s_in.name = "UNSEEN";
    791   else if ((res = expectThisString("DRAFT")) == ACCEPT) s_in.name = "DRAFT";
    792   else if ((res = expectThisString("HEADER")) == ACCEPT) { 
    793     s_in.name = "HEADER";
    794 	
    795     if ((res = expectSPACE()) != ACCEPT) {
    796       session.setLastError("Expected SPACE");
    797       return res;
    798     }
    799 	
    800     if ((res = expectAstring(s_in.astring)) != ACCEPT) {
    801       session.setLastError("Expected astring");
    802       return res;
    803     }
    804 
    805     if ((res = expectSPACE()) != ACCEPT) {
    806       session.setLastError("Expected SPACE");
    807       return res;
    808     }
    809 	
    810     if ((res = expectAstring(s_in.bstring)) != ACCEPT) {
    811       session.setLastError("Expected astring");
    812       return res;
    813     }
    814   } else if ((res = expectThisString("LARGER")) == ACCEPT) {
    815     s_in.name = "LARGER";
    816     if ((res = expectSPACE()) != ACCEPT) {
    817       session.setLastError("Expected SPACE");
    818       return res;
    819     }
    820 	
    821     if ((res = expectNumber(s_in.number)) != ACCEPT) {
    822       session.setLastError("Expected number");
    823       return res;
    824     }
    825   } else if ((res = expectThisString("NOT")) == ACCEPT) {
    826     s_in.name = "NOT";
    827     s_in.type = BincImapParserSearchKey::KEY_NOT;
    828 	
    829     if ((res = expectSPACE()) != ACCEPT) {
    830       session.setLastError("Expected SPACE");
    831       return res;
    832     }
    833 
    834     BincImapParserSearchKey s;
    835     if ((res = expectSearchKey(s)) != ACCEPT) {
    836       session.setLastError("Expected search_key");
    837       return res;
    838     }
    839     s_in.children.push_back(s);
    840   } else if ((res = expectThisString("OR")) == ACCEPT) {
    841     s_in.name = "OR";
    842     s_in.type = BincImapParserSearchKey::KEY_OR;
    843 	
    844     if ((res = expectSPACE()) != ACCEPT) {
    845       session.setLastError("Expected SPACE");
    846       return res;
    847     }
    848 
    849     BincImapParserSearchKey s;
    850     if ((res = expectSearchKey(s)) != ACCEPT) {
    851       session.setLastError("Expected search_key");
    852       return res;
    853     }
    854     s_in.children.push_back(s);
    855 
    856     if ((res = expectSPACE()) != ACCEPT) {
    857       session.setLastError("Expected SPACE");
    858       return res;
    859     }
    860 
    861     BincImapParserSearchKey t;
    862     if ((res = expectSearchKey(t)) != ACCEPT) {
    863       session.setLastError("Expected search_key");
    864       return res;
    865     }
    866     s_in.children.push_back(t);
    867   } else if ((res = expectThisString("SENTBEFORE")) == ACCEPT) {
    868     s_in.name = "SENTBEFORE";
    869 	
    870     if ((res = expectSPACE()) != ACCEPT) {
    871       session.setLastError("Expected SPACE");
    872       return res;
    873     }
    874 	
    875     if ((res = expectDate(s_in.date)) != ACCEPT) {
    876       session.setLastError("Expected date");
    877       return res;
    878     }
    879   } else if ((res = expectThisString("SENTON")) == ACCEPT) {
    880     s_in.name = "SENTON";
    881 	
    882     if ((res = expectSPACE()) != ACCEPT) {
    883       session.setLastError("Expected SPACE");
    884       return res;
    885     }
    886 	
    887     if ((res = expectDate(s_in.date)) != ACCEPT) {
    888       session.setLastError("Expected date");      
    889       return res;
    890     }
    891 
    892   } else if ((res = expectThisString("SENTSINCE")) == ACCEPT) {
    893     s_in.name = "SENTSINCE";
    894 	
    895     if ((res = expectSPACE()) != ACCEPT) {
    896       session.setLastError("Expected SPACE");
    897       return res;
    898     }
    899 	
    900     if ((res = expectDate(s_in.date)) != ACCEPT) {
    901       session.setLastError("Expected date");
    902       return res;
    903     }
    904   } else if ((res = expectThisString("SMALLER")) == ACCEPT) {
    905     s_in.name = "SMALLER";
    906     if ((res = expectSPACE()) != ACCEPT) {
    907       session.setLastError("Expected SPACE");
    908       return res;
    909     }
    910 	
    911     if ((res = expectNumber(s_in.number)) != ACCEPT) {
    912       session.setLastError("Expected number");
    913       return res;
    914     }
    915   } else if ((res = expectThisString("UID")) == ACCEPT) {
    916     s_in.name = "UID";
    917     if ((res = expectSPACE()) != ACCEPT) {
    918       session.setLastError("Expected SPACE");
    919       return res;
    920     }
    921 	
    922     if ((res = expectSet(s_in.bset)) != ACCEPT) {
    923       session.setLastError("Expected number");
    924       return res;
    925     }
    926   } else if ((res = expectThisString("UNDRAFT")) == ACCEPT) s_in.name = "UNDRAFT";
    927   else if ((res = expectSet(s_in.bset)) == ACCEPT) {
    928     s_in.name = "";
    929     s_in.type = BincImapParserSearchKey::KEY_SET;
    930   } else if ((res = expectThisString("(")) == ACCEPT) {
    931     s_in.type = BincImapParserSearchKey::KEY_AND;
    932 
    933     while (1) {
    934       BincImapParserSearchKey c;
    935       if ((res = expectSearchKey(c)) != ACCEPT) {
    936 	session.setLastError("Expected search_key");
    937 	return res;
    938       }
    939 	 
    940       s_in.children.push_back(c);
    941 	  
    942       if ((res = expectSPACE()) != ACCEPT)
    943 	break;
    944     }
    945 
    946     if ((res = expectThisString(")")) != ACCEPT) {
    947       session.setLastError("Expected )");
    948       return res;
    949     }
    950   } else
    951     return REJECT;
    952     
    953   return ACCEPT;
    954 }