3 using std::experimental::string_view;
6 constexpr size_t BUFSIZE = 10 * 4 * 1024;
16 CHECK_NOTNULL(mb_loc_beg);
19 mb_loc = string(mb_loc_beg, mb_loc_end - mb_loc_beg);
30 CHECK_NOTNULL(mb_dom_beg);
33 mb_dom = string(mb_dom_beg, mb_dom_end - mb_dom_beg);
39 action magic_postmaster {
40 mb_loc = string("Postmaster");
45 param.first += static_cast<char>(::toupper(fc));
53 parameters.insert(param);
62 # action chunk_size_beg {
66 # action chunk_size_end {
68 # chunk_sz = stoll(string(chunk_sz_beg, chunk_sz_end - chunk_sz_beg));
69 # chunk_sz_beg = nullptr;
70 # chunk_sz_end = nullptr;
73 #############################################################################
75 UTF8_tail = 0x80..0xBF;
79 UTF8_2 = 0xC2..0xDF UTF8_tail;
81 UTF8_3 = (0xE0 0xA0..0xBF UTF8_tail)
82 | (0xE1..0xEC UTF8_tail{2})
83 | (0xED 0x80..0x9F UTF8_tail)
84 | (0xEE..0xEF UTF8_tail{2})
87 UTF8_4 = (0xF0 0x90..0xBF UTF8_tail{2})
88 | (0xF1..0xF3 UTF8_tail{3})
89 | (0xF4 0x80..0x8F UTF8_tail{2})
92 # UTF8_char = UTF8_1 | UTF8_2 | UTF8_3 | UTF8_4;
94 UTF8_non_ascii = UTF8_2 | UTF8_3 | UTF8_4;
96 # various definitions from RFC 5234
108 Let_dig = alpha | digit;
110 Ldh_str = (alpha | digit | '-')* Let_dig;
112 U_Let_dig = alpha | digit | UTF8_non_ascii;
114 U_Ldh_str = (alpha | digit | '-' | UTF8_non_ascii)* U_Let_dig;
116 U_label = U_Let_dig U_Ldh_str?;
118 label = Let_dig Ldh_str?;
120 sub_domain = label | U_label;
122 Domain = sub_domain ('.' sub_domain)*;
124 snum = ('2' '5' '0'..'5')
125 | ('2' '0'..'4' digit)
126 | ('0'..'1' digit{1,2})
130 IPv4_address_literal = snum ('.' snum){3};
132 IPv6_hex = xdigit{1,4};
134 IPv6_full = IPv6_hex (':' IPv6_hex){7};
136 IPv6_comp = (IPv6_hex (':' IPv6_hex){0,5})? '::' (IPv6_hex (':' IPv6_hex){0,5})?;
138 IPv6v4_full = IPv6_hex (':' IPv6_hex){5} ':' IPv4_address_literal;
140 IPv6v4_comp = (IPv6_hex (':' IPv6_hex){0,3})? '::' (IPv6_hex (':' IPv6_hex){0,3} ':')? IPv4_address_literal;
142 IPv6_addr = IPv6_full | IPv6_comp | IPv6v4_full | IPv6v4_comp;
144 IPv6_address_literal = 'IPv6:' IPv6_addr;
146 dcontent = graph - '\[' - '\\' - '\]'; # 33..90 | 94..126
148 standardized_tag = Ldh_str;
150 General_address_literal = standardized_tag ':' dcontent{1};
152 # See rfc 5321 Section 4.1.3
153 address_literal = '[' (IPv4_address_literal |
154 IPv6_address_literal | General_address_literal) ']';
156 At_domain = '@' Domain;
158 A_d_l = At_domain (',' At_domain)*;
160 qtextSMTP = print - '"' - '\\' | UTF8_non_ascii;
162 quoted_pairSMTP = '\\' print;
164 QcontentSMTP = qtextSMTP | quoted_pairSMTP;
166 Quoted_string = '"' QcontentSMTP* '"';
168 atext = alpha | digit |
181 # obs_FWS = WSP+ (CRLF WSP+)*;
183 # FWS = ((WSP* CRLF)? WSP+) | obs_FWS;
185 # ccontent = ctext | quoted_pair | comment;
186 # comment = '(' (FWS? ccontent)* FWS? ')';
187 # CFWS = ((FWS? comment)+ FWS?) | FWS;
189 # Atom = cfws atext+ cfws;
193 Dot_string = Atom ('.' Atom)*;
195 Local_part = Dot_string | Quoted_string;
197 Mailbox = Local_part >mb_loc_beg %mb_loc_end '@'
198 ((Domain | address_literal) >mb_dom_beg %mb_dom_end);
200 Path = "<" ((A_d_l ":")? Mailbox) ">";
202 Reverse_path = Path | "<>";
204 Forward_path = Path | "<Postmaster>"i %magic_postmaster;
206 esmtp_keyword = ((alpha | digit) (alpha | digit | '-')*) @key_end;
208 esmtp_value = ((graph - '=' | UTF8_non_ascii)+) @val_end;
210 esmtp_param = (esmtp_keyword ('=' esmtp_value)?) %param;
212 Mail_parameters = esmtp_param (' ' esmtp_param)*;
214 Rcpt_parameters = esmtp_param (' ' esmtp_param)*;
216 String = Atom | Quoted_string;
220 #############################################################################
224 /[^\.\r\n]/ /[^\r\n]/* CRLF =>
226 auto len = te - ts - 2; // minus crlf
227 msg.out().write(ts, len);
229 msg_bytes += len + 1;
232 '.' /[^\r\n]/+ CRLF =>
234 auto len = te - ts - 3; // minus crlf and leading '.'
235 msg.out().write(ts + 1, len);
237 msg_bytes += len + 1;
248 session.data_msg_done(msg);
249 if (msg_bytes > Config::size) {
250 LOG(WARNING) << "message size " << msg_bytes << " exceeds maximium of " << Config::size;
257 #............................................................................
261 "AUTH LOGIN"i CRLF =>
263 session.error("AUTH not supported");
266 "EHLO"i SP (Domain | address_literal) CRLF =>
268 char const* beg = ts + 5;
269 char const* end = te - 2;
270 session.ehlo(string(beg, end - beg));
273 "HELO"i SP Domain CRLF =>
275 char const* beg = ts + 5;
276 char const* end = te - 2;
277 session.helo(string(beg, end - beg));
280 # optional space not specified by RFC 5321
281 "MAIL FROM:"i SP? Reverse_path (SP Mail_parameters)? CRLF =>
283 session.mail_from(Mailbox(mb_loc, mb_dom), parameters);
290 # optional space not specified by RFC 5321
291 "RCPT TO:"i SP? Forward_path (SP Rcpt_parameters)? CRLF =>
293 session.rcpt_to(Mailbox(mb_loc, mb_dom), parameters);
302 if (session.data_start()) {
303 session.data_msg(msg);
305 LOG(INFO) << "calling data\n";
310 # "BDAT"i SP (chunk_size >chunk_size_beg %chunk_size_end) (SP "LAST"i @last)? CRLF =>
312 # LOG(INFO) << "BDAT " << chunk_sz << (last ? " LAST" : "");
314 # // eat data from our buffer
315 # auto space = pe - ts;
316 # LOG(INFO) << "space == " << space;
329 "NOOP"i (SP String)? CRLF =>
334 "VRFY"i (SP String)? CRLF =>
339 "HELP"i (SP String)? CRLF =>
358 %% write data nofinal;
360 //...........................................................................
362 void scanner(Session& session)
367 // char const* chunk_sz_beg{nullptr};
368 // char const* chunk_sz_end{nullptr};
369 // size_t chunk_sz{0};
371 char const* mb_loc_beg{nullptr};
372 char const* mb_loc_end{nullptr};
373 char const* mb_dom_beg{nullptr};
374 char const* mb_dom_end{nullptr};
381 std::pair<string, string> param;
382 std::unordered_map<string, string> parameters;
384 static char buf[BUFSIZE];
394 #pragma GCC diagnostic ignored "-Wunused-but-set-variable"
397 char const* eof = nullptr;
403 std::streamsize space = BUFSIZE - have;
406 // We've used up the entire buffer storing an already-parsed token
407 // prefix that must be preserved.
408 session.error("out of buffer space");
409 LOG(FATAL) << "out of buffer space";
412 char* p = buf + have;
413 std::streamsize len = session.read(p, space);
415 if (len == -1) { // EOF processing
423 // Check if this is the end of file.
426 LOG(INFO) << "no more input";
427 std::exit(EXIT_SUCCESS);
434 LOG(INFO) << "exec \'" << string_view(buf, have + len) << "'";
436 #pragma GCC diagnostic push
437 #pragma GCC diagnostic ignored "-Wold-style-cast"
441 #pragma GCC diagnostic pop
443 if (cs == smtp_error) {
444 session.error("parse error");
452 // There is a prefix to preserve, shift it over.
455 memmove(buf, ts, have);
458 auto delta = ts - buf;
465 te = buf + (te - ts);
471 int main(int argc, char const* argv[])
473 close(2); // hackage to stop glog from spewing
475 std::ios::sync_with_stdio(false);
477 auto logdir = getenv("GOOGLE_LOG_DIR");
479 boost::system::error_code ec;
480 boost::filesystem::create_directories(logdir, ec); // ignore errors;
482 google::InitGoogleLogging(argv[0]);
488 LOG(INFO) << "scanner returned";