// imapd/parse_imap_command.y // This file is part of Decimail; see http://decimail.org // (C) 2004-2017 Philip Endecott // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. %{ #include "CommandParser.hh" #include "commands.hh" #include "limits.h" #include #include #include int parse_imap_command_lex(YYSTYPE* lvalp, yyscan_t* scanner_p) { return scan_imap_command_lex(lvalp, *scanner_p); } #define session (((ParserParams*)params)->session) #define scanner (((ParserParams*)params)->scanner) #define tag (((ParserParams*)params)->tag) #define start_atom (((ParserParams*)params)->scanner_extra->start_atom) void parse_imap_command_error(void* params, const char* msg) { session->respond_bad(tag,msg); session->flush(); } #undef yylineno %} %parse-param {void* params} %lex-param {*scanner} %file-prefix "parse_imap_command" %name-prefix "parse_imap_command_" %pure-parser %debug %error-verbose %verbose %defines %union { int num; std::string* str; ci_string* cistr; char chr; std::set* cistrset; Imapd::Command* cmd; Imapd::UidableCmd* uidablecmd; Imapd::MessageSet* msgset; Imapd::FetchAtt* fetch_att; std::list* fetch_att_list; Imapd::FetchBodySection::Section* section; Imapd::FetchBodySection::Range* range; Imapd::StoreCmd::FlagChangeType store_flag_change; Imapd::SearchKey* search_key; std::list* search_key_list; } %token SPACE PLUS MINUS LPAREN RPAREN LANGLE RANGLE LBRACKET RBRACKET DOT %token PERCENT STAR COLON COMMA BACKSLASH CRLF END_OF_FILE %token DIGIT %token ATOM QUOTED LITERAL %token KW_ALL KW_ANSWERED KW_APPEND KW_AUTHENTICATE KW_BCC KW_BEFORE KW_BODY %token KW_CAPABILITY KW_CC KW_CHARSET KW_CHECK KW_CLOSE KW_COPY KW_CREATE %token KW_DELETE KW_DELETED KW_DRAFT KW_ENVELOPE KW_EXAMINE KW_EXPUNGE KW_FAST %token KW_FETCH KW_FIELDS KW_FLAGGED KW_FLAGS KW_FULL KW_FROM KW_HEADER KW_IDLE %token KW_INBOX %token KW_INTERNALDATE KW_KEYWORD KW_LARGER KW_LIST KW_LOGIN KW_LOGOUT KW_LSUB %token KW_MESSAGES KW_MIME KW_NEW KW_NOOP KW_NOT KW_OLD KW_ON KW_OR KW_PEEK %token KW_RECENT KW_RENAME KW_RFC822 KW_SEARCH KW_SEEN KW_SELECT KW_SENTBEFORE %token KW_SENTON KW_SENTSINCE KW_SILENT KW_SINCE KW_SIZE KW_SMALLER KW_STATUS %token KW_STORE KW_STRUCTURE KW_SUBSCRIBE KW_SUBJECT KW_TEXT KW_TO KW_UID %token KW_UIDNEXT KW_UIDVALIDITY KW_UNANSWERED KW_UNDELETED KW_UNDRAFT %token KW_UNFLAGGED KW_UNKEYWORD KW_UNSEEN KW_UNSUBSCRIBE %token KW_XCOMPLETE %token KW_JAN KW_FEB KW_MAR KW_APR KW_MAY KW_JUN KW_JUL KW_AUG KW_SEP KW_OCT %token KW_NOV KW_DEC %token ILLEGAL UNTERMINATED_STRING %type string astring astring_1 atom tag mailbox mailbox_1 %type password userid auth_type list_mailbox list_mailbox_1 %type list_mailbox_2 list_mailbox_elem opt_date_time date_time %type flag_keyword date opt_charset %type header_fld_name %type number nz_number sequence_num %type status_att_list status_att %type opt_flag_list flag_list flag_list_body flag_list_body_1 %type flag system_flag %type system_flag_name flag_list_maybep flag_list_noparen %type opt_dot_peek opt_dot_not opt_dot_silent %type header_list header_list_body %type append authenticate command_1 command_any command_auth %type command_nonauth command_select create delete examine %type idle list login lsub rename select status %type subscribe uid unsubscribe xcomplete %type copy fetch search store uid_subcmd %type set %type fetch_item_names fetch_att_list %type fetch_att fetch_att_rfc822_qualifier %type fetch_att_body_section %type
section opt_subsection_spec subsection_spec section_text %type opt_byterange %type opt_plusminus %type search_key %type search_key_list %destructor { delete $$; } QUOTED LITERAL ATOM string astring astring_1 atom tag mailbox mailbox_1 password userid auth_type list_mailbox list_mailbox_1 list_mailbox_2 list_mailbox_elem opt_date_time date_time header_fld_name header_list header_list_body append authenticate command_1 command_any command_auth command_nonauth command_select create delete examine idle list login lsub rename select status subscribe uid unsubscribe copy fetch search store uid_subcmd set fetch_att fetch_att_rfc822_qualifier fetch_att_body_section section opt_subsection_spec subsection_spec section_text opt_byterange %destructor { list* l = static_cast*>($$); for(list::iterator i=l->begin(); i!=l->end(); ++i) { delete *i; } delete l; } fetch_item_names fetch_att_list %start command_seq %% append: KW_APPEND SPACE mailbox opt_flag_list opt_date_time SPACE LITERAL { $$=new Imapd::AppendCmd(tag,*$3,$4,*$5,*$7); delete $3; delete $5; delete $7; } ; opt_flag_list: /*nothing*/ { $$ = 0; } | SPACE flag_list { $$ = $2; } ; opt_date_time: /*nothing*/ { $$ = new std::string(""); } | SPACE date_time { $$ = $2; } ; astring: { start_atom=1; } astring_1 { start_atom=0; $$ = $2; } ; astring_1: ATOM { $$ = $1; } | string { $$ = $1; } ; atom: { start_atom=1; } ATOM { start_atom=0; $$ = $2; } ; authenticate: KW_AUTHENTICATE SPACE auth_type authenticate_data { $$=new Imapd::AuthenticateCmd(tag,*$3 /*...*/); delete $3; } ; authenticate_data: /*nothing*/ | CRLF base64 authenticate_data ; auth_type: atom { $$ = $1; } ; base64: /*todo*/ ; command: tag SPACE { tag = *$1; delete $1; } command_1 CRLF { if ($4) { $4->run(*session); delete $4; yyerrok; } } | CRLF /* blank line not per RFC, but allow it */ | error CRLF { yyerrok; } ; command_1: command_any { $$ = $1; } | command_auth { $$ = $1; } | command_nonauth { $$ = $1; } | command_select { $$ = $1; } | error { $$=NULL; } ; command_any: KW_CAPABILITY { $$=new Imapd::CapabilityCmd(tag); } | KW_LOGOUT { $$=new Imapd::LogoutCmd(tag); } | KW_NOOP { $$=new Imapd::NoopCmd(tag); } ; command_auth: append { $$ = $1; } | create { $$ = $1; } | delete { $$ = $1; } | examine { $$ = $1; } | idle { $$ = $1; } | list { $$ = $1; } | lsub { $$ = $1; } | rename { $$ = $1; } | select { $$ = $1; } | status { $$ = $1; } | subscribe { $$ = $1; } | unsubscribe { $$ = $1; } | xcomplete { $$ = $1; } ; command_nonauth: login { $$ = $1; } | authenticate { $$ = $1; } ; command_select: KW_CHECK { $$=new Imapd::CheckCmd(tag); } | KW_CLOSE { $$=new Imapd::CloseCmd(tag); } | KW_EXPUNGE { $$=new Imapd::ExpungeCmd(tag); } | copy { $$ = $1; } | fetch { $$ = $1; } | store { $$ = $1; } | uid { $$ = $1; } | search { $$ = $1; } ; command_seq: { start_atom=1; } command_seq_1 { YYACCEPT; }; command_seq_1: END_OF_FILE | command command_seq ; copy: KW_COPY SPACE set SPACE mailbox { $$=new Imapd::CopyCmd(tag,$3,*$5); delete $5; } ; create: KW_CREATE SPACE mailbox { $$=new Imapd::CreateCmd(tag,*$3); delete $3; } ; date: date_text { $$ = new std::string("please quote dates!"); } | QUOTED { $$ = $1; }; date_day: DIGIT | DIGIT DIGIT ; /* date_day_fixed: SPACE DIGIT | DIGIT DIGIT ; */ date_month: KW_JAN | KW_FEB | KW_MAR | KW_APR | KW_MAY | KW_JUN | KW_JUL | KW_AUG | KW_SEP | KW_OCT | KW_NOV | KW_DEC ; date_text: date_day MINUS date_month MINUS date_year ; date_time: QUOTED { $$ = $1; } ; date_year: DIGIT DIGIT DIGIT DIGIT ; delete: KW_DELETE SPACE mailbox { $$=new Imapd::DeleteCmd(tag,*$3); delete $3; } ; examine: KW_EXAMINE SPACE mailbox { $$=new Imapd::ExamineCmd(tag,*$3); delete $3; } ; fetch: KW_FETCH SPACE set SPACE fetch_item_names { $$=new Imapd::FetchCmd(tag,$3,*$5); delete $5; } ; fetch_item_names: KW_ALL { $$ = new std::list; $$->push_back(new Imapd::FetchFlags()); $$->push_back(new Imapd::FetchInternaldate()); $$->push_back(new Imapd::FetchRfc822Size()); $$->push_back(new Imapd::FetchEnvelope()); } | KW_FULL { $$ = new std::list; $$->push_back(new Imapd::FetchFlags()); $$->push_back(new Imapd::FetchInternaldate()); $$->push_back(new Imapd::FetchRfc822Size()); $$->push_back(new Imapd::FetchEnvelope()); $$->push_back(new Imapd::FetchBody()); } | KW_FAST { $$ = new std::list; $$->push_back(new Imapd::FetchFlags()); $$->push_back(new Imapd::FetchInternaldate()); $$->push_back(new Imapd::FetchRfc822Size()); } | fetch_att { $$ = new std::list; $$->push_back($1); } | LPAREN fetch_att_list RPAREN { $$ = $2; } ; fetch_att_list: fetch_att { $$ = new std::list; $$->push_back($1); } | fetch_att_list SPACE fetch_att { $1->push_back($3); } ; fetch_att: KW_ENVELOPE { $$ = new Imapd::FetchEnvelope(); } | KW_FLAGS { $$ = new Imapd::FetchFlags(); } | KW_INTERNALDATE { $$ = new Imapd::FetchInternaldate(); } | KW_RFC822 fetch_att_rfc822_qualifier { $$ = $2; } | KW_BODY { $$ = new Imapd::FetchBody(); } | KW_BODY KW_STRUCTURE { $$ = new Imapd::FetchBodystructure(); } | KW_UID { $$ = new Imapd::FetchUid(); } | fetch_att_body_section { $$ = $1; } ; fetch_att_rfc822_qualifier: /*nothing*/ { $$ = new Imapd::FetchRfc822(); } | DOT KW_HEADER { $$ = new Imapd::FetchRfc822Header(); } | DOT KW_SIZE { $$ = new Imapd::FetchRfc822Size(); } | DOT KW_TEXT { $$ = new Imapd::FetchRfc822Text(); } ; fetch_att_body_section: KW_BODY opt_dot_peek section opt_byterange { $$ = new Imapd::FetchBodySection($2,$3,$4); } ; opt_dot_peek: /*nothing*/ { $$ = 0; } | DOT KW_PEEK { $$ = 1; } ; opt_byterange: /*nothing*/ { $$ = new Imapd::FetchBodySection::UnlimitedRange(); } | LANGLE number DOT nz_number RANGLE { $$ = new Imapd::FetchBodySection::LimitedRange($2,$4); } flag: system_flag { $$ = $1; } | flag_keyword { parse_imap_command_error(params,"flag keywords not supported"); } | flag_extension { parse_imap_command_error(params,"flag extensions not supported"); } ; system_flag: BACKSLASH system_flag_name { $$ = $2; } ; system_flag_name: KW_ANSWERED { $$ = Imapd::answered; } | KW_FLAGGED { $$ = Imapd::flagged; } | KW_DELETED { $$ = Imapd::deleted; } | KW_SEEN { $$ = Imapd::seen; } | KW_DRAFT { $$ = Imapd::draft; } ; flag_extension: BACKSLASH atom ; flag_keyword: atom { $$ = $1; } ; flag_list: LPAREN flag_list_body RPAREN { $$ = $2; } ; flag_list_body: /*nothing*/ { $$ = 0; } | flag_list_body_1 { $$ = $1; } ; flag_list_body_1: flag { $$ = $1; } | flag SPACE flag_list_body_1 { $$ = $1 | $3; } ; header_fld_name: astring { $$ = new ci_string($1->c_str()); delete $1; } ; header_list: LPAREN header_list_body RPAREN { $$ = $2; } ; header_list_body: header_fld_name { $$ = new std::set; $$->insert(*$1); delete $1; } | header_list_body SPACE header_fld_name { $$ = $1; $1->insert(*$3); delete $3; } ; idle: KW_IDLE { $$=new Imapd::IdleCmd(tag); } ; list: KW_LIST SPACE mailbox SPACE list_mailbox { $$=new Imapd::ListCmd(tag,*$3,*$5); delete $3; delete $5; } ; list_mailbox: { start_atom=1; } list_mailbox_1 { $$ = $2; } ; list_mailbox_1: KW_INBOX { $$ = new std::string("INBOX"); } | string { $$ = $1; } | list_mailbox_elem { start_atom=1; } list_mailbox_2 { $$ = $1; $$->append(*$3); delete $3 ; } ; list_mailbox_2: /*nothing*/ { $$ = new std::string(""); } | list_mailbox_elem { start_atom=1; } list_mailbox_2 { $$ = $1; $$->append(*$3); delete $3; } ; list_mailbox_elem: ATOM { $$ = $1; } | STAR { $$ = new std::string(1,'*'); } | PERCENT { $$ = new std::string(1,'%'); } ; login: KW_LOGIN SPACE userid SPACE password { $$=new Imapd::LoginCmd(tag,*$3,*$5); delete $3; delete $5; } ; lsub: KW_LSUB SPACE mailbox SPACE list_mailbox { $$=new Imapd::LsubCmd(tag,*$3,*$5); delete $3; delete $5; } ; mailbox: { start_atom=1; } mailbox_1 { $$ = $2; } ; mailbox_1: KW_INBOX { $$ = new std::string("INBOX"); } | astring { $$ = $1; } ; number: DIGIT { $$ = $1 - '0'; } | number DIGIT { /* Should allow up to 2^32; is 2^31-10 ok? */ if ($1>((INT_MAX/10)-10)) { yyerror(params,"Number too large"); } $$ = $1*10 + $2 - '0'; } ; nz_number: number { $$ = $1; if($$==0) { yyerror(params,"Zero not allowed here"); YYERROR; } } ; password: astring { $$ = $1; } ; rename: KW_RENAME SPACE mailbox SPACE mailbox { $$=new Imapd::RenameCmd(tag,*$3,*$5); delete $3; delete $5; } ; search: KW_SEARCH SPACE opt_charset search_key_list { $$=new Imapd::SearchCmd(tag,*$3,*$4); delete $3; } ; opt_charset: /* nothing */ { $$ = new std::string(""); } | KW_CHARSET SPACE astring SPACE { $$ = $3; } ; search_key_list: search_key { $$ = new std::list; $$->push_back($1); } | search_key SPACE search_key_list { $$ = $3; $$ -> push_back($1); } ; search_key: KW_ALL { $$ = new SearchKeyAll; } | KW_ANSWERED { $$ = new SearchKeyFlag("\\Answered"); } | KW_BCC SPACE string { $$ = new SearchKeyAddr("bcc",*$3); delete $3; } | KW_BEFORE SPACE date { $$ = new SearchKeyDateBefore(*$3); delete $3; } | KW_BODY SPACE astring { $$ = new SearchKeyBody(*$3); delete $3; } | KW_CC SPACE astring { $$ = new SearchKeyAddr("cc",*$3); delete $3; } | KW_DELETED { $$ = new SearchKeyFlag("\\Deleted"); } | KW_FLAGGED { $$ = new SearchKeyFlag("\\Flagged"); } | KW_FROM SPACE astring { $$ = new SearchKeyFrom(*$3); delete $3; } | KW_KEYWORD SPACE flag_keyword { $$ = new SearchKeyFlag(*$3); delete $3; } | KW_NEW /* = (RECENT UNSEEN) */ { $$ = new SearchKeyNotImplemented; } | KW_OLD /* = NOT RECENT */ { $$ = new SearchKeyNotImplemented; } | KW_ON SPACE date { $$ = new SearchKeyDateSameDay(*$3); delete $3; } | KW_RECENT /* won't work, we have no recent flag */ { $$ = new SearchKeyFlag("\\Recent");; } | KW_SEEN { $$ = new SearchKeyNoFlag("\\Unseen"); } | KW_SINCE SPACE date { $$ = new SearchKeyDateOnOrAfter(*$3); delete $3; } | KW_SUBJECT SPACE astring { $$ = new SearchKeySubject(*$3); delete $3; } | KW_TEXT SPACE astring { $$ = new SearchKeyText(*$3); delete $3; } | KW_TO SPACE astring { $$ = new SearchKeyAddr("to",*$3); delete $3; } | KW_UNANSWERED { $$ = new SearchKeyNoFlag("\\Answered"); } | KW_UNDELETED { $$ = new SearchKeyNoFlag("\\Deleted"); } | KW_UNFLAGGED { $$ = new SearchKeyNoFlag("\\Flagged"); } | KW_UNKEYWORD SPACE flag_keyword { $$ = new SearchKeyNoFlag(*$3); delete $3; } | KW_UNSEEN { $$ = new SearchKeyFlag("\\Unseen"); } | KW_DRAFT { $$ = new SearchKeyFlag("\\Draft"); } | KW_HEADER SPACE header_fld_name SPACE astring { $$ = new SearchKeyHeader(*$3,*$5); delete $3; delete $5; } | KW_LARGER SPACE number { $$ = new SearchKeySizeLarger($3); } | KW_NOT SPACE search_key { $$ = new SearchKeyNot($3); } | KW_OR SPACE search_key SPACE search_key { $$ = new SearchKeyOr($3,$5); } | KW_SENTBEFORE SPACE date { $$ = new SearchKeySentBefore(*$3); delete $3; } | KW_SENTON SPACE date { $$ = new SearchKeySentSameDay(*$3); delete $3; } | KW_SENTSINCE SPACE date { $$ = new SearchKeySentOnOrAfter(*$3); delete $3; } | KW_SMALLER SPACE number { $$ = new SearchKeySizeSmaller($3); } | KW_UID SPACE set { $$ = new SearchKeyUid($3); } | KW_UNDRAFT { $$ = new SearchKeyNoFlag("\\Draft"); } | set { $$ = new SearchKeyMsgset($1); } | LPAREN search_key_list RPAREN { $$ = new SearchKeySubexpression(*$2); delete $2; } ; section: LBRACKET opt_subsection_spec RBRACKET { $$ = $2; } ; opt_subsection_spec: /*nothing*/ { $$ = NULL; } | subsection_spec { $$ = $1; } ; subsection_spec: nz_number { $$ = new Imapd::FetchBodySection::SectionNumbered($1); } | section_text { $$ = $1; } | nz_number DOT subsection_spec { $$ = new Imapd::FetchBodySection::SectionNumbered($1,$3); } ; section_text: KW_HEADER { $$ = new Imapd::FetchBodySection::SectionHeader(); } | KW_HEADER DOT KW_FIELDS opt_dot_not SPACE header_list { $$ = new Imapd::FetchBodySection::SectionHeaderFields($4,*$6); delete $6; } | KW_TEXT { $$ = new Imapd::FetchBodySection::SectionText(); } | KW_MIME { $$ = new Imapd::FetchBodySection::SectionMime(); } ; /* The RFC BNF is stricter, it does not allow MIME except for subsections. */ opt_dot_not: /*nothing*/ { $$ = 0; } | DOT KW_NOT { $$ = 1; } ; select: KW_SELECT SPACE mailbox { $$=new Imapd::SelectCmd(tag,*$3); delete $3; } ; sequence_num: nz_number { $$ = $1; } | STAR { $$ = 0; } ; set: sequence_num { $$ = new Imapd::MessageSetSingleton($1); } | sequence_num COLON sequence_num { $$ = new Imapd::MessageSetRange($1,$3); } | set COMMA set { $$ = new Imapd::MessageSetSet($1,$3); } ; status: KW_STATUS SPACE mailbox SPACE LPAREN status_att_list RPAREN { $$=new Imapd::StatusCmd(tag,*$3,$6); delete $3; } ; status_att_list: status_att { $$ = $1; } | status_att SPACE status_att_list { $$ = $1 | $3; } ; status_att: KW_MESSAGES { $$ = Imapd::messages; } | KW_RECENT { $$ = Imapd::recent; } | KW_UIDNEXT { $$ = Imapd::uidnext; } | KW_UIDVALIDITY { $$ = Imapd::uidvalidity; } | KW_UNSEEN { $$ = Imapd::unseen; } ; store: KW_STORE SPACE set SPACE opt_plusminus KW_FLAGS opt_dot_silent SPACE flag_list_maybep { $$=new Imapd::StoreCmd(tag,$3,$5,$7,$9); } ; opt_plusminus: /*nothing*/ { $$ = Imapd::StoreCmd::set; } | PLUS { $$ = Imapd::StoreCmd::add; } | MINUS { $$ = Imapd::StoreCmd::remove; } ; opt_dot_silent: /*nothing*/ { $$ = 0; } | DOT KW_SILENT { $$ = 1; } ; flag_list_maybep: flag_list { $$ = $1; } | flag_list_noparen { $$ = $1; } ; flag_list_noparen: flag { $$=$1; } | flag SPACE flag_list_noparen { $$=$1|$3; } ; string: QUOTED { $$ = $1; } | LITERAL { $$ = $1; } ; subscribe: KW_SUBSCRIBE SPACE mailbox { $$=new Imapd::SubscribeCmd(tag,*$3); delete $3; } ; tag: atom { $$ = $1; } ; /* Reject if it contains a + ! */ /* time: DIGIT DIGIT COLON DIGIT DIGIT COLON DIGIT DIGIT ; */ uid: KW_UID SPACE uid_subcmd { $$=new Imapd::UidCmd(tag,$3); } ; uid_subcmd: copy { $$ = $1; } | fetch { $$ = $1; } | search { $$ = $1; } | store { $$ = $1; } ; unsubscribe: KW_UNSUBSCRIBE SPACE mailbox { $$=new Imapd::UnsubscribeCmd(tag,*$3); delete $3; } ; userid: astring { $$ = $1; } ; xcomplete: KW_XCOMPLETE SPACE number SPACE astring SPACE astring { $$=new Imapd::CompleteCmd(tag,$3,*$5,*$7); delete $5; delete $7; } ; /* zone: zone_plusminus DIGIT DIGIT DIGIT DIGIT ; */ /* zone_plusminus: PLUS | MINUS ; */ %%