/** * @file recursivedescent.cc * @brief Implementation of a recursive descent IMAP command parser. * @author Andreas Aardal Hanssen * @date 2002-2005 */ #include "recursivedescent.h" #include "convert.h" #include "imapparser.h" #include "iodevice.h" #include "iofactory.h" #include #include #include #include using std::string; using ParseResult = Binc::Parser::ParseResult; constexpr auto ERROR = ParseResult::ERROR; constexpr auto REJECT = ParseResult::REJECT; constexpr auto ACCEPT = ParseResult::ACCEPT; constexpr auto TIMEOUT = ParseResult::TIMEOUT; ParseResult Binc::Parser::expectThisString(const string &s_in) { char c; bool match = true; for (string::const_iterator i = s_in.begin(); i != s_in.end(); ++i) { if (!bincClient.readChar(&c)) { session.setLastError(bincClient.getLastErrorString()); if (bincClient.getLastError() == IODevice::Error::Timeout) return Parser::ParseResult::TIMEOUT; return ERROR; } if (toupper(*i) != toupper(c)) { match = false; break; } } if (!match) { bincClient.unreadChar(c); return REJECT; } else { return ACCEPT; } } ParseResult Binc::Parser::expectDateTime(string &s_in) { if (expectThisString("\"") != ACCEPT) return REJECT; unsigned int digit1, digit2; if (expectSPACE() == ACCEPT) { digit1 = 0; ParseResult res; if ((res = expectDigit(digit2)) != ACCEPT) { session.setLastError("expected digit (day) after \" and a SPACE."); return res; } } else { ParseResult res; if ((res = expectDigit(digit1)) != ACCEPT) { session.setLastError("expected first digit of day"); return res; } if ((res = expectDigit(digit2)) != ACCEPT) { session.setLastError("expected second digit of day"); return res; } } int day = digit1 * 10 + digit2; BincStream daystr; if (day < 10) daystr << '0'; daystr << day; s_in += daystr.str(); ParseResult res; if ((res = expectThisString("-")) != ACCEPT) { session.setLastError("expected -"); return res; } s_in += "-"; /* month */ if ((res = expectThisString("Jan")) == ACCEPT) { s_in += "Jan"; } else if ((res = expectThisString("Feb")) == ACCEPT) { s_in += "Feb"; } else if ((res = expectThisString("Mar")) == ACCEPT) { s_in += "Mar"; } else if ((res = expectThisString("Apr")) == ACCEPT) { s_in += "Apr"; } else if ((res = expectThisString("May")) == ACCEPT) { s_in += "May"; } else if ((res = expectThisString("Jun")) == ACCEPT) { s_in += "Jun"; } else if ((res = expectThisString("Jul")) == ACCEPT) { s_in += "Jul"; } else if ((res = expectThisString("Aug")) == ACCEPT) { s_in += "Aug"; } else if ((res = expectThisString("Sep")) == ACCEPT) { s_in += "Sep"; } else if ((res = expectThisString("Oct")) == ACCEPT) { s_in += "Oct"; } else if ((res = expectThisString("Nov")) == ACCEPT) { s_in += "Nov"; } else if ((res = expectThisString("Dec")) == ACCEPT) { s_in += "Dec"; } else { session.setLastError("expected month"); return res; } if ((res = expectThisString("-")) != ACCEPT) { session.setLastError("expected -"); return res; } s_in += "-"; /* year */ unsigned int year, c; if ((res = expectDigit(year)) != ACCEPT) { session.setLastError("expected digit (first digit of year)"); return res; } if ((res = expectDigit(c)) != ACCEPT) { session.setLastError("expected digit (second digit of year)"); return res; } year = (year * 10) + c; if ((res = expectDigit(c)) != ACCEPT) { session.setLastError("expected digit (third digit of year)"); return res; } year = (year * 10) + c; if ((res = expectDigit(c)) != ACCEPT) { session.setLastError("expected digit (last digit of year)"); return res; } year = (year * 10) + c; BincStream yearstr; yearstr << year; s_in += yearstr.str(); if ((res = expectSPACE()) != ACCEPT) { session.setLastError("expected SPACE"); return res; } s_in += " "; if ((res = expectTime(s_in)) != ACCEPT) { session.setLastError("expected time"); return res; } if ((res = expectSPACE()) != ACCEPT) { session.setLastError("expected SPACE"); return res; } s_in += " "; if ((res = expectZone(s_in)) != ACCEPT) { session.setLastError("expected zone"); return res; } if ((res = expectThisString("\"")) != ACCEPT) { session.setLastError("expected \""); return res; } return ACCEPT; } ParseResult Binc::Parser::expectTime(string &s_in) { unsigned int c, t; ParseResult res; if ((res = expectDigit(t)) != ACCEPT) return res; if ((res = expectDigit(c)) != ACCEPT) { session.setLastError("expected digit"); return res; } t = (t * 10) + c; BincStream tstr; tstr << t; s_in += tstr.str(); if ((res = expectThisString(":")) != ACCEPT) { session.setLastError("expected colon"); return res; } s_in += ":"; if ((res = expectDigit(t)) != ACCEPT) { session.setLastError("expected digit"); return res; } if ((res = expectDigit(c)) != ACCEPT) { session.setLastError("expected digit"); return res; } t = (t * 10) + c; tstr.clear(); tstr << t; s_in += tstr.str(); if ((res = expectThisString(":")) != ACCEPT) { session.setLastError("expected colon"); return res; } s_in += ":"; if ((res = expectDigit(t)) != ACCEPT) { session.setLastError("expected digit"); return res; } if ((res = expectDigit(c)) != ACCEPT) { session.setLastError("expected digit"); return res; } t = (t * 10) + c; tstr.clear(); tstr << t; s_in += tstr.str(); return ACCEPT; } ParseResult Binc::Parser::expectZone(string &s_in) { ParseResult res; if ((res = expectThisString("-")) == ACCEPT) s_in += "-"; else if ((res = expectThisString("+")) == ACCEPT) s_in += "+"; else return res; unsigned int c, t; if ((res = expectDigit(t)) != ACCEPT) { session.setLastError("expected digit"); return res; } if ((res = expectDigit(c)) != ACCEPT) { session.setLastError("expected digit"); return res; } t = (t * 10) + c; if ((res = expectDigit(c)) != ACCEPT) { session.setLastError("expected digit"); return res; } t = (t * 10) + c; if ((res = expectDigit(c)) != ACCEPT) { session.setLastError("expected digit"); return res; } t = (t * 10) + c; BincStream tstr; tstr << t; s_in += tstr.str(); return ACCEPT; } ParseResult Binc::Parser::expectListWildcards(int &c_in) { ParseResult res; if ((res = expectThisString("%")) == ACCEPT) { c_in = '%'; return ACCEPT; } else if ((res = expectThisString("*")) == ACCEPT) { c_in = '*'; return ACCEPT; } else { return res; } } ParseResult Binc::Parser::expectListMailbox(string &s_in) { ParseResult res; if ((res = expectString(s_in)) == ACCEPT) return ACCEPT; int c; if ((res = expectAtomChar(c)) == ACCEPT || (res = expectListWildcards(c)) == ACCEPT || (res = expectThisString("]")) == ACCEPT) { do { s_in += (char)c; if ((res = expectAtomChar(c)) != ACCEPT && (res = expectListWildcards(c)) != ACCEPT && (res = expectThisString("]")) != ACCEPT) { return ACCEPT; } } while (1); } bincClient.unreadStr(s_in); return res; } ParseResult Binc::Parser::expectFlag(std::vector &v_in) { ParseResult res; string flag; if ((res = expectThisString("\\Answered")) == ACCEPT) { v_in.push_back("\\Answered"); } else if ((res = expectThisString("\\Flagged")) == ACCEPT) { v_in.push_back("\\Flagged"); } else if ((res = expectThisString("\\Deleted")) == ACCEPT) { v_in.push_back("\\Deleted"); } else if ((res = expectThisString("\\Seen")) == ACCEPT) { v_in.push_back("\\Seen"); } else if ((res = expectThisString("\\Draft")) == ACCEPT) { v_in.push_back("\\Draft"); } else if ((res = expectThisString("\\Answered")) == ACCEPT) { v_in.push_back("\\Answered"); } else if ((res = expectThisString("\\")) == ACCEPT) { if ((res = expectAtom(flag)) == ACCEPT) { v_in.push_back("\\" + flag); } else { session.setLastError("expected atom"); return res; } } else if (expectAtom(flag) == ACCEPT) { v_in.push_back(flag); } else { return res; } return ACCEPT; } ParseResult Binc::Parser::expectDate(string &s_in) { ParseResult res; bool quoted = false; if ((res = expectThisString("\"")) == ACCEPT) quoted = true; /* day */ unsigned int day, c; if ((res = expectDigit(c)) == ACCEPT) { day = c; if ((res = expectDigit(c)) == ACCEPT) day = (day * 10) + c; BincStream daystr; daystr << day; s_in += daystr.str(); } else { session.setLastError("expected digit"); return res; } /* - */ if ((res = expectThisString("-")) != ACCEPT) { session.setLastError("expected -"); return res; } s_in += '-'; /* month */ if ((res = expectThisString("Jan")) == ACCEPT) { s_in += "Jan"; } else if ((res = expectThisString("Feb")) == ACCEPT) { s_in += "Feb"; } else if ((res = expectThisString("Mar")) == ACCEPT) { s_in += "Mar"; } else if ((res = expectThisString("Apr")) == ACCEPT) { s_in += "Apr"; } else if ((res = expectThisString("May")) == ACCEPT) { s_in += "May"; } else if ((res = expectThisString("Jun")) == ACCEPT) { s_in += "Jun"; } else if ((res = expectThisString("Jul")) == ACCEPT) { s_in += "Jul"; } else if ((res = expectThisString("Aug")) == ACCEPT) { s_in += "Aug"; } else if ((res = expectThisString("Sep")) == ACCEPT) { s_in += "Sep"; } else if ((res = expectThisString("Oct")) == ACCEPT) { s_in += "Oct"; } else if ((res = expectThisString("Nov")) == ACCEPT) { s_in += "Nov"; } else if ((res = expectThisString("Dec")) == ACCEPT) { s_in += "Dec"; } else { session.setLastError("expected month"); return res; } /* - */ if ((res = expectThisString("-")) != ACCEPT) { session.setLastError("expected -"); return res; } s_in += '-'; /* year */ unsigned int year; if ((res = expectDigit(year)) != ACCEPT) { session.setLastError("expected digit"); return res; } if ((res = expectDigit(c)) != ACCEPT) { session.setLastError("expected digit"); return res; } year = (year * 10) + c; if ((res = expectDigit(c)) != ACCEPT) { session.setLastError("expected digit"); return res; } year = (year * 10) + c; if ((res = expectDigit(c)) != ACCEPT) { session.setLastError("expected digit"); return res; } year = (year * 10) + c; BincStream yearstr; yearstr << year; s_in += yearstr.str(); if (quoted) { if ((res = expectThisString("\"")) != ACCEPT) { session.setLastError("expected \""); return res; } } return ACCEPT; } ParseResult Binc::Parser::expectCRLF() { ParseResult res; if ((res = expectCR()) == ACCEPT && (res = expectLF()) == ACCEPT) return ACCEPT; else return res; } ParseResult Binc::Parser::expectCR() { char c; if (!bincClient.readChar(&c)) { session.setLastError(bincClient.getLastErrorString()); if (bincClient.getLastError() == IODevice::Error::Timeout) return TIMEOUT; return ERROR; } if (c == 0x0d) { return ACCEPT; } else { bincClient.unreadChar(c); return REJECT; } } ParseResult Binc::Parser::expectLF() { char c; if (!bincClient.readChar(&c)) { session.setLastError(bincClient.getLastErrorString()); if (bincClient.getLastError() == IODevice::Error::Timeout) return TIMEOUT; return ERROR; } if (c == 0x0a) { return ACCEPT; } else { bincClient.unreadChar(c); return REJECT; } } ParseResult Binc::Parser::expectTagChar(int &c_in) { char c; if (!bincClient.readChar(&c)) { session.setLastError(bincClient.getLastErrorString()); if (bincClient.getLastError() == IODevice::Error::Timeout) return TIMEOUT; return ERROR; } switch (c) { case 041: case 043: case 044: case 046: case 047: case 054: case 055: case 056: case 057: case 060: case 061: case 062: case 063: case 064: case 065: case 066: case 067: case 070: case 071: case 072: case 073: case 074: case 075: case 076: case 077: case 0100: case 0101: case 0102: case 0103: case 0104: case 0105: case 0106: case 0107: case 0110: case 0111: case 0112: case 0113: case 0114: case 0115: case 0116: case 0117: case 0120: case 0121: case 0122: case 0123: case 0124: case 0125: case 0126: case 0127: case 0130: case 0131: case 0132: case 0133: case 0135: case 0136: case 0137: case 0140: case 0141: case 0142: case 0143: case 0144: case 0145: case 0146: case 0147: case 0150: case 0151: case 0152: case 0153: case 0154: case 0155: case 0156: case 0157: case 0160: case 0161: case 0162: case 0163: case 0164: case 0165: case 0166: case 0167: case 0170: case 0171: case 0172: case 0174: case 0175: case 0176: c_in = c; return ACCEPT; default: break; } bincClient.unreadChar(c); return REJECT; } ParseResult Binc::Parser::expectTag(string &s_in) { string tag; int tagchar; ParseResult eres = expectTagChar(tagchar); if (eres == REJECT) { return REJECT; } else if (eres == ERROR) { return ERROR; } else if (eres == TIMEOUT) { return TIMEOUT; } else { tag += tagchar; bool done = false; while (!done) { switch (expectTagChar(tagchar)) { case ACCEPT: tag += tagchar; break; case REJECT: done = true; break; case ERROR: return ERROR; case TIMEOUT: return TIMEOUT; } } } s_in = tag; return ACCEPT; } ParseResult Binc::Parser::expectSPACE() { char c; if (!bincClient.readChar(&c)) { session.setLastError(bincClient.getLastErrorString()); if (bincClient.getLastError() == IODevice::Error::Timeout) return TIMEOUT; return ERROR; } if (c == ' ') { return ACCEPT; } else { bincClient.unreadChar(c); return REJECT; } } ParseResult Binc::Parser::expectMailbox(string &s_in) { return expectAstring(s_in); } // FIXME: This rule is wrong. ParseResult Binc::Parser::expectAstring(string &s_in) { ParseResult res; if ((res = expectAtom(s_in)) == ACCEPT) return ACCEPT; if ((res = expectString(s_in)) == ACCEPT) return ACCEPT; return res; } ParseResult Binc::Parser::expectAtomChar(int &c_in) { char c; if (!bincClient.readChar(&c)) { session.setLastError(bincClient.getLastErrorString()); if (bincClient.getLastError() == IODevice::Error::Timeout) return TIMEOUT; return ERROR; } switch (c) { case 041: case 043: case 044: case 046: case 047: case 053: case 054: case 055: case 056: case 057: case 060: case 061: case 062: case 063: case 064: case 065: case 066: case 067: case 070: case 071: case 072: case 073: case 074: case 075: case 076: case 077: case 0100: case 0101: case 0102: case 0103: case 0104: case 0105: case 0106: case 0107: case 0110: case 0111: case 0112: case 0113: case 0114: case 0115: case 0116: case 0117: case 0120: case 0121: case 0122: case 0123: case 0124: case 0125: case 0126: case 0127: case 0130: case 0131: case 0132: case 0133: case 0135: case 0136: case 0137: case 0140: case 0141: case 0142: case 0143: case 0144: case 0145: case 0146: case 0147: case 0150: case 0151: case 0152: case 0153: case 0154: case 0155: case 0156: case 0157: case 0160: case 0161: case 0162: case 0163: case 0164: case 0165: case 0166: case 0167: case 0170: case 0171: case 0172: case 0174: case 0175: case 0176: c_in = c; return ACCEPT; default: break; } bincClient.unreadChar(c); return REJECT; } ParseResult Binc::Parser::expectAtom(string &s_in) { string atom; int atomchar; ParseResult res; while ((res = expectAtomChar(atomchar)) == ACCEPT) atom += atomchar; if (atom == "") { bincClient.unreadStr(atom); return res; } else { s_in = atom; } return ACCEPT; } ParseResult Binc::Parser::expectString(string &s_in) { ParseResult res; if ((res = expectQuoted(s_in)) == ACCEPT) return ACCEPT; if ((res = expectLiteral(s_in)) == ACCEPT) return ACCEPT; return res; } ParseResult Binc::Parser::expectQuoted(string &s_in) { string quoted; int quotedchar; ParseResult res; if ((res = expectThisString("\"")) != ACCEPT) return res; while ((res = expectQuotedChar(quotedchar)) == ACCEPT) quoted += quotedchar; if ((res = expectThisString("\"")) != ACCEPT) { bincClient.unreadStr("\"" + quoted); return res; } s_in = quoted; return ACCEPT; } ParseResult Binc::Parser::expectQuotedChar(int &c_in) { char c; if (!bincClient.readChar(&c)) { session.setLastError(bincClient.getLastErrorString()); if (bincClient.getLastError() == IODevice::Error::Timeout) return TIMEOUT; return ERROR; } switch (c) { case 01: case 02: case 03: case 04: case 05: case 06: case 07: case 010: case 011: case 013: case 014: case 016: case 017: case 020: case 021: case 022: case 023: case 024: case 025: case 026: case 027: case 030: case 031: case 032: case 033: case 034: case 035: case 036: case 037: case 040: case 041: case 043: case 044: case 045: case 046: case 047: case 050: case 051: case 052: case 053: case 054: case 055: case 056: case 057: case 060: case 061: case 062: case 063: case 064: case 065: case 066: case 067: case 070: case 071: case 072: case 073: case 074: case 075: case 076: case 077: case 0100: case 0101: case 0102: case 0103: case 0104: case 0105: case 0106: case 0107: case 0110: case 0111: case 0112: case 0113: case 0114: case 0115: case 0116: case 0117: case 0120: case 0121: case 0122: case 0123: case 0124: case 0125: case 0126: case 0127: case 0130: case 0131: case 0132: case 0133: case 0135: case 0136: case 0137: case 0140: case 0141: case 0142: case 0143: case 0144: case 0145: case 0146: case 0147: case 0150: case 0151: case 0152: case 0153: case 0154: case 0155: case 0156: case 0157: case 0160: case 0161: case 0162: case 0163: case 0164: case 0165: case 0166: case 0167: case 0170: case 0171: case 0172: case 0173: case 0174: case 0175: case 0176: case 0177: c_in = c; return ACCEPT; case '\\': { char d; if (!bincClient.readChar(&d)) { session.setLastError(bincClient.getLastErrorString()); if (bincClient.getLastError() == IODevice::Error::Timeout) return TIMEOUT; return ERROR; } if (d == '\"' || d == '\\') { c_in = d; return ACCEPT; } else { bincClient.unreadChar(d); bincClient.unreadChar(c); return REJECT; } } default: break; } bincClient.unreadChar(c); return REJECT; } ParseResult Binc::Parser::expectLiteral(string &s_in) { string literal; ParseResult res; if ((res = expectThisString("{")) != ACCEPT) return res; unsigned int nchar; if ((res = expectNumber(nchar)) != ACCEPT) { session.setLastError("expected number"); return res; } // rfc2088 describes the non-synchronizing literal, or LITERAL+, as // sent by the client with an extra '+' appended after the octet // count. bool literalPlus = false; if ((res = expectThisString("+")) == ACCEPT) literalPlus = true; if ((res = expectThisString("}")) != ACCEPT) { session.setLastError("expected }"); return res; } if ((res = expectCRLF()) != ACCEPT) { session.setLastError("expected CRLF"); return ERROR; } // Only send the reply if the client did not send a LITERAL+ // request. if (!literalPlus) { bincClient << "+ ok, send " << nchar << " bytes of data." << std::endl; bincClient.flush(); } for (unsigned int i = 0; i < nchar; ++i) { char c; if (!bincClient.readChar(&c)) { session.setLastError(bincClient.getLastErrorString()); if (bincClient.getLastError() == IODevice::Error::Timeout) return TIMEOUT; return ERROR; } literal += c; } s_in = literal; return ACCEPT; } ParseResult Binc::Parser::expectNumber(unsigned int &i_in) { i_in = 0; unsigned int n; ParseResult res; while ((res = expectDigit(n)) == ACCEPT) if (i_in == 0) i_in = n; else i_in = (i_in * 10) + n; if (res == TIMEOUT) return res; return ACCEPT; } ParseResult Binc::Parser::expectDigit(unsigned int &i_in) { char c; if (!bincClient.readChar(&c)) { session.setLastError(bincClient.getLastErrorString()); if (bincClient.getLastError() == IODevice::Error::Timeout) return TIMEOUT; return ERROR; } if (c == '0') { i_in = 0; return ACCEPT; } else { bincClient.unreadChar(c); } if (expectDigitNZ(i_in) != ACCEPT) return REJECT; return ACCEPT; } ParseResult Binc::Parser::expectDigitNZ(unsigned int &i_in) { char c; if (!bincClient.readChar(&c)) { session.setLastError(bincClient.getLastErrorString()); if (bincClient.getLastError() == IODevice::Error::Timeout) return TIMEOUT; return ERROR; } switch (c) { case '1': i_in = 1; break; case '2': i_in = 2; break; case '3': i_in = 3; break; case '4': i_in = 4; break; case '5': i_in = 5; break; case '6': i_in = 6; break; case '7': i_in = 7; break; case '8': i_in = 8; break; case '9': i_in = 9; break; case -1: session.setLastError(bincClient.getLastErrorString()); return ERROR; case -2: return TIMEOUT; default: bincClient.unreadChar(c); return REJECT; } return ACCEPT; } ParseResult Binc::Parser::expectSet(SequenceSet &s_in) { unsigned int seqnum = (unsigned int)-1; ParseResult res; /* if a set does not start with a sequencenum, then it's not a * set. :-) seqnum == -1 means '*'. */ if ((res = expectSequenceNum(seqnum)) != ACCEPT) return res; /* the first number is always a part of the set */ s_in.addNumber(seqnum); /* if _after_ a set there is a ':', then there will always be a * sequencenum after the colon. if not, it's a syntax error. a * colon delimits two numbers in a range. */ if ((res = expectThisString(":")) == ACCEPT) { unsigned int seqnum2 = (unsigned int)-1; if ((res = expectSequenceNum(seqnum2)) != ACCEPT) { session.setLastError("expected sequencenum"); return res; } s_in.addRange(seqnum, seqnum2); } /* if _after_ a set there is a ',', then there will always be * a set after the comma. if not, it's a syntax error. */ if ((res = expectThisString(",")) == ACCEPT) { if ((res = expectSet(s_in)) != ACCEPT) { session.setLastError("expected set"); return res; } } return ACCEPT; } ParseResult Binc::Parser::expectSequenceNum(unsigned int &i_in) { char c; if (!bincClient.readChar(&c)) { session.setLastError(bincClient.getLastErrorString()); if (bincClient.getLastError() == IODevice::Error::Timeout) return TIMEOUT; return ERROR; } if (c == '*') { i_in = (unsigned int)-1; return ACCEPT; } else { bincClient.unreadChar(c); } if (expectNZNumber(i_in) != ACCEPT) return REJECT; else return ACCEPT; } ParseResult Binc::Parser::expectNZNumber(unsigned int &i_in) { unsigned int c; ParseResult res; if ((res = expectDigitNZ(c)) != ACCEPT) return res; i_in = c; while ((res = expectDigit(c)) == ACCEPT) i_in = (i_in * 10) + c; if (res == TIMEOUT) return res; return ACCEPT; }