/// <summary> /// Try to make an Esmtp-Parameter from the tokens. /// </summary> /// <param name="enumerator">The enumerator to perform the make on.</param> /// <param name="parameter">The esmtp-parameter that was made.</param> /// <returns>true if the esmtp-parameter can be made, false if not.</returns> /// <remarks><![CDATA[esmtp-keyword ["=" esmtp-value]]]></remarks> public bool TryMakeEsmtpParameter(TokenEnumerator enumerator, out KeyValuePair <string, string> parameter) { parameter = default(KeyValuePair <string, string>); string keyword; if (TryMake(enumerator, TryMakeEsmtpKeyword, out keyword) == false) { return(false); } if (enumerator.Peek().Kind == TokenKind.None || enumerator.Peek().Kind == TokenKind.Space) { parameter = new KeyValuePair <string, string>(keyword, null); return(true); } if (enumerator.Peek() != new Token(TokenKind.Symbol, "=")) { return(false); } enumerator.Take(); string value; if (TryMake(enumerator, TryMakeEsmtpValue, out value) == false) { return(false); } parameter = new KeyValuePair <string, string>(keyword, value); return(true); }
/// <summary> /// Make an AUTH command from the given enumerator. /// </summary> /// <param name="enumerator">The enumerator to create the command from.</param> /// <param name="command">The AUTH command that is defined within the token enumerator.</param> /// <param name="errorResponse">The error that indicates why the command could not be made.</param> /// <returns>Returns true if a command could be made, false if not.</returns> public bool TryMakeAuth(TokenEnumerator enumerator, out SmtpCommand command, out SmtpResponse errorResponse) { Debug.Assert(enumerator.Peek() == new Token(TokenKind.Text, "AUTH")); command = null; errorResponse = null; enumerator.Take(); enumerator.TakeWhile(TokenKind.Space); AuthenticationMethod method; if (Enum.TryParse(enumerator.Peek().Text, true, out method) == false) { _logger.LogVerbose("AUTH command requires a valid method (PLAIN or LOGIN)"); errorResponse = SmtpResponse.SyntaxError; return(false); } enumerator.Take(); string parameter = null; if (enumerator.Count > 0 && _parser.TryMakeBase64(enumerator, out parameter) == false) { _logger.LogVerbose("AUTH parameter must be a Base64 encoded string"); errorResponse = SmtpResponse.SyntaxError; return(false); } command = new AuthCommand(_options.UserAuthenticator, method, parameter); return(true); }
internal bool TryMakeEsmtpKeyword(TokenEnumerator enumerator, out string keyword) { keyword = null; Token left = enumerator.Peek(0); while (left.Kind == TokenKind.Text || left.Kind == TokenKind.Number || left == new Token(TokenKind.Punctuation, "-")) { keyword += enumerator.Consume(1).Text; left = enumerator.Peek(0); } return(keyword != null); }
internal bool TryMakeTextOrNumberOrHyphenString(TokenEnumerator enumerator, out string textOrNumberOrHyphenString) { textOrNumberOrHyphenString = null; Token left = enumerator.Peek(0); while (left.Kind == TokenKind.Text || left.Kind == TokenKind.Number || left == new Token(TokenKind.Punctuation, "-")) { textOrNumberOrHyphenString += enumerator.Consume(1).Text; left = enumerator.Peek(0); } return(textOrNumberOrHyphenString != null && left != new Token(TokenKind.Punctuation, "-")); }
/// <summary> /// Try to make an Esmtp-Keyword from the tokens. /// </summary> /// <param name="enumerator">The enumerator to perform the make on.</param> /// <param name="keyword">The esmtp-keyword that was made.</param> /// <returns>true if the esmtp-keyword can be made, false if not.</returns> /// <remarks><![CDATA[(ALPHA / DIGIT) *(ALPHA / DIGIT / "-")]]></remarks> public bool TryMakeEsmtpKeyword(TokenEnumerator enumerator, out string keyword) { keyword = null; var token = enumerator.Peek(); while (token.Kind == TokenKind.Text || token.Kind == TokenKind.Number || token == new Token(TokenKind.Punctuation, "-")) { keyword += enumerator.Take().Text; token = enumerator.Peek(); } return(keyword != null); }
/// <summary> /// Try to make an Esmtp-Value from the tokens. /// </summary> /// <param name="enumerator">The enumerator to perform the make on.</param> /// <param name="value">The esmtp-value that was made.</param> /// <returns>true if the esmtp-value can be made, false if not.</returns> /// <remarks><![CDATA[1*(%d33-60 / %d62-127)]]></remarks> public bool TryMakeEsmtpValue(TokenEnumerator enumerator, out string value) { value = null; var token = enumerator.Peek(); while (token.Text.Length > 0 && token.Text.ToCharArray().All(ch => (ch >= 33 && ch <= 66) || (ch >= 62 && ch <= 127))) { value += enumerator.Take().Text; token = enumerator.Peek(); } return(value != null); }
/// <summary> /// Make an EHLO command from the given enumerator. /// </summary> /// <param name="enumerator">The enumerator to create the command from.</param> /// <param name="command">The EHLO command that is defined within the token enumerator.</param> /// <param name="errorResponse">The error that indicates why the command could not be made.</param> /// <returns>Returns true if a command could be made, false if not.</returns> public bool TryMakeEhlo(TokenEnumerator enumerator, out SmtpCommand command, out SmtpResponse errorResponse) { Debug.Assert(enumerator.Peek() == new Token(TokenKind.Text, "EHLO")); command = null; errorResponse = null; enumerator.Take(); enumerator.TakeWhile(TokenKind.Space); string domain; if (_parser.TryMakeDomain(enumerator, out domain)) { command = new EhloCommand(domain, _options); return(true); } string address; if (_parser.TryMakeAddressLiteral(enumerator, out address)) { command = new EhloCommand(address, _options); return(true); } errorResponse = SmtpResponse.SyntaxError; return(false); }
internal bool TryMakeEsmtpValue(TokenEnumerator enumerator, out string value) { value = null; Token token = enumerator.Peek(0); while (token.Text.Length > 0) { if (!token.Text.ToCharArray().All((char ch) => (ch >= '!' && ch <= 'B') || (ch >= '>' && ch <= '\u007f'))) { break; } value += enumerator.Consume(1).Text; token = enumerator.Peek(0); } return(value != null); }
/// <summary> /// Make a RCTP command from the given enumerator. /// </summary> /// <param name="enumerator">The enumerator to create the command from.</param> /// <param name="command">The RCTP command that is defined within the token enumerator.</param> /// <param name="errorResponse">The error that indicates why the command could not be made.</param> /// <returns>Returns true if a command could be made, false if not.</returns> public bool TryMakeRcpt(TokenEnumerator enumerator, out SmtpCommand command, out SmtpResponse errorResponse) { Debug.Assert(enumerator.Peek() == new Token(TokenKind.Text, "RCPT")); command = null; errorResponse = null; enumerator.Take(); enumerator.TakeWhile(TokenKind.Space); if (enumerator.Take() != new Token(TokenKind.Text, "TO") || enumerator.Take() != new Token(TokenKind.Punctuation, ":")) { errorResponse = new SmtpResponse(SmtpReplyCode.SyntaxError, "missing the TO:"); return(false); } // according to the spec, whitespace isnt allowed here anyway enumerator.TakeWhile(TokenKind.Space); IMailbox mailbox; if (_parser.TryMakePath(enumerator, out mailbox) == false) { _logger.LogVerbose("Syntax Error (Text={0})", enumerator.AsText()); errorResponse = SmtpResponse.SyntaxError; return(false); } // TODO: support optional service extension parameters here command = new RcptCommand(mailbox, _options.MailboxFilterFactory); return(true); }
/// <summary> /// Try to make a text/number/hyphen string. /// </summary> /// <param name="enumerator">The enumerator to make the text/number/hyphen from.</param> /// <param name="textOrNumberOrHyphenString">The text, number, or hyphen that was matched, or undefined if it was not matched.</param> /// <returns>true if a text, number or hyphen was made, false if not.</returns> /// <remarks><![CDATA[*( ALPHA / DIGIT / "-" ) Let-dig]]></remarks> public bool TryMakeTextOrNumberOrHyphenString(TokenEnumerator enumerator, out string textOrNumberOrHyphenString) { textOrNumberOrHyphenString = null; var token = enumerator.Peek(); while (token.Kind == TokenKind.Text || token.Kind == TokenKind.Number || token == new Token(TokenKind.Punctuation, "-")) { textOrNumberOrHyphenString += enumerator.Take().Text; token = enumerator.Peek(); } // can not end with a hyphen return(textOrNumberOrHyphenString != null && token != new Token(TokenKind.Punctuation, "-")); }
/// <summary> /// Try to make an IPv4 address literal. /// </summary> /// <param name="enumerator">The enumerator to make the address from.</param> /// <param name="address">The address that was made, or undefined if it was not made.</param> /// <returns>true if the address was made, false if not.</returns> /// <remarks><![CDATA[ Snum 3("." Snum) ]]></remarks> public bool TryMakeIpv4AddressLiteral(TokenEnumerator enumerator, out string address) { address = null; int snum; if (TryMake(enumerator, TryMakeSnum, out snum) == false) { return(false); } address = snum.ToString(CultureInfo.InvariantCulture); for (var i = 0; i < 3 && enumerator.Peek() == new Token(TokenKind.Punctuation, "."); i++) { enumerator.Take(); if (TryMake(enumerator, TryMakeSnum, out snum) == false) { return(false); } address = String.Concat(address, '.', snum); } return(true); }
public void EnumerateTokens() { var tokenizer = new TokenEnumerator(new ByteArrayTokenReader(Segments)); while (tokenizer.Peek() != Token.None) { tokenizer.Take(); } }
internal bool TryMakeBase64(TokenEnumerator enumerator, out string base64) { base64 = null; while (enumerator.Peek(0).Kind != TokenKind.None) { string str; if (!TryMake <string>(enumerator, new TryMakeDelegate <string>(TryMakeBase64Chars), out str)) { return(false); } base64 += str; } return(base64 != null && base64.Length % 4 == 0); }
internal bool TryMakeMailParameters(TokenEnumerator enumerator, out IDictionary <string, string> parameters) { parameters = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); while (enumerator.Peek(0).Kind != TokenKind.None) { KeyValuePair <string, string> item; if (!TryMake <KeyValuePair <string, string> >(enumerator, new TryMakeDelegate <KeyValuePair <string, string> >(TryMakeEsmtpParameter), out item)) { return(false); } parameters.Add(item); enumerator.ConsumeWhile((Token t) => t.Kind == TokenKind.Space); } return(parameters.Count > 0); }
internal bool TryMakeAtDomainList(TokenEnumerator enumerator, out string atDomainList) { if (!TryMake <string>(enumerator, new TryMakeDelegate <string>(TryMakeAtDomain), out atDomainList)) { return(false); } while (enumerator.Peek(0) == new Token(TokenKind.Punctuation, ",")) { enumerator.Consume(1); string arg; if (!TryMake <string>(enumerator, new TryMakeDelegate <string>(TryMakeAtDomain), out arg)) { return(false); } atDomainList += string.Format(",{0}", arg); } return(true); }
internal bool TryMakeDotString(TokenEnumerator enumerator, out string dotString) { if (!TryMake <string>(enumerator, new TryMakeDelegate <string>(TryMakeAtom), out dotString)) { return(false); } while (enumerator.Peek(0) == new Token(TokenKind.Punctuation, ".")) { enumerator.Consume(1); string str; if (!TryMake <string>(enumerator, new TryMakeDelegate <string>(TryMakeAtom), out str)) { return(true); } dotString += "." + str; } return(true); }
internal bool TryAccept(TokenEnumerator tokenEnumerator, out SmtpCommand command) { Tuple <State.MakeDelegate, int> tuple; if (!_state.Actions.TryGetValue(tokenEnumerator.Peek(0).Text, out tuple)) { string response = string.Format("expected {0}", string.Join("/", _state.Actions.Keys)); command = _commandFactory.MakeInvalid(SmtpReplyCode.SyntaxError, response); return(false); } command = tuple.Item1(tokenEnumerator); if (!(command is InvalidCommand)) { _state = _states[tuple.Item2]; return(true); } return(false); }
/// <summary> /// Try to make an Mail-Parameters from the tokens. /// </summary> /// <param name="enumerator">The enumerator to perform the make on.</param> /// <param name="parameters">The mail parameters that were made.</param> /// <returns>true if the mail parameters can be made, false if not.</returns> /// <remarks><![CDATA[esmtp-param *(SP esmtp-param)]]></remarks> public bool TryMakeMailParameters(TokenEnumerator enumerator, out IDictionary <string, string> parameters) { parameters = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); while (enumerator.Peek().Kind != TokenKind.None) { KeyValuePair <string, string> parameter; if (TryMake(enumerator, TryMakeEsmtpParameter, out parameter) == false) { return(false); } parameters.Add(parameter); enumerator.TakeWhile(t => t.Kind == TokenKind.Space); } return(parameters.Count > 0); }
internal SmtpCommand MakeAuth(TokenEnumerator enumerator) { enumerator.Consume(1); enumerator.ConsumeWhile((Token t) => t.Kind == TokenKind.Space); AuthenticationMethod method; if (!Enum.TryParse(enumerator.Peek(0).Text, true, out method)) { return(MakeInvalid(SmtpReplyCode.SyntaxError, "")); } enumerator.Consume(1); string parameter = null; if (enumerator.Count > 0 && !_parser.TryMakeBase64(enumerator, out parameter)) { return(MakeInvalid(SmtpReplyCode.SyntaxError, "")); } return(new AuthCommand(_server.UserAuthenticator, method, parameter)); }
/// <summary> /// Try to make a base64 encoded string. /// </summary> /// <param name="enumerator">The enumerator to perform the make on.</param> /// <param name="base64">The base64 encoded string that were found.</param> /// <returns>true if the base64 encoded string can be made, false if not.</returns> /// <remarks><![CDATA[ALPHA / DIGIT / "+" / "/"]]></remarks> public bool TryMakeBase64(TokenEnumerator enumerator, out string base64) { base64 = null; while (enumerator.Peek().Kind != TokenKind.None) { string base64Chars; if (TryMake(enumerator, TryMakeBase64Chars, out base64Chars) == false) { return(false); } base64 += base64Chars; } // because the TryMakeBase64Chars method matches tokens, each Text token could make // up several Base64 encoded "bytes" so we ensure that we have a length divisible by 4 return(base64 != null && base64.Length % 4 == 0); }
/// <summary> /// Advances the enumerator to the next command in the stream. /// </summary> /// <param name="context">The session context to use for making session based transitions.</param> /// <param name="tokenEnumerator">The token enumerator to accept the command from.</param> /// <param name="command">The command that is defined within the token enumerator.</param> /// <param name="errorResponse">The error that indicates why the command could not be made.</param> /// <returns>true if a valid command was found, false if not.</returns> public bool TryMake(SmtpSessionContext context, TokenEnumerator tokenEnumerator, out SmtpCommand command, out SmtpResponse errorResponse) { if (_states[_current].Transitions.TryGetValue(tokenEnumerator.Peek().Text, out _transition) == false) { var response = $"expected {String.Join("/", _states[_current].Transitions.Keys)}"; command = null; errorResponse = new SmtpResponse(SmtpReplyCode.SyntaxError, response); return(false); } if (_transition.Delegate(tokenEnumerator, out command, out errorResponse) == false) { return(false); } return(true); }
/// <summary> /// Make a MAIL command from the given enumerator. /// </summary> /// <param name="enumerator">The enumerator to create the command from.</param> /// <param name="command">The MAIL command that is defined within the token enumerator.</param> /// <param name="errorResponse">The error that indicates why the command could not be made.</param> /// <returns>Returns true if a command could be made, false if not.</returns> public bool TryMakeMail(TokenEnumerator enumerator, out SmtpCommand command, out SmtpResponse errorResponse) { Debug.Assert(enumerator.Peek() == new Token(TokenKind.Text, "MAIL")); command = null; errorResponse = null; enumerator.Take(); enumerator.TakeWhile(TokenKind.Space); if (enumerator.Take() != new Token(TokenKind.Text, "FROM") || enumerator.Take() != new Token(TokenKind.Punctuation, ":")) { errorResponse = new SmtpResponse(SmtpReplyCode.SyntaxError, "missing the FROM:"); return(false); } // according to the spec, whitespace isnt allowed here but most servers send it enumerator.TakeWhile(TokenKind.Space); IMailbox mailbox; if (_parser.TryMakeReversePath(enumerator, out mailbox) == false) { _logger.LogVerbose("Syntax Error (Text={0})", enumerator.AsText()); errorResponse = new SmtpResponse(SmtpReplyCode.SyntaxError); return(false); } enumerator.TakeWhile(TokenKind.Space); // match the optional (ESMTP) parameters IReadOnlyDictionary <string, string> parameters; if (_parser.TryMakeMailParameters(enumerator, out parameters) == false) { parameters = new Dictionary <string, string>(); } command = new MailCommand(_options, mailbox, parameters); return(true); }
/// <summary> /// Make an RSET command from the given enumerator. /// </summary> /// <param name="enumerator">The enumerator to create the command from.</param> /// <param name="command">The RSET command that is defined within the token enumerator.</param> /// <param name="errorResponse">The error that indicates why the command could not be made.</param> /// <returns>Returns true if a command could be made, false if not.</returns> public bool TryMakeRset(TokenEnumerator enumerator, out SmtpCommand command, out SmtpResponse errorResponse) { Debug.Assert(enumerator.Peek() == new Token(TokenKind.Text, "RSET")); command = null; errorResponse = null; enumerator.Take(); enumerator.TakeWhile(TokenKind.Space); if (enumerator.Count > 1) { _logger.LogVerbose("RSET command can not have parameters (found {0} parameters).", enumerator.Count); errorResponse = SmtpResponse.SyntaxError; return(false); } command = RsetCommand.Instance; return(true); }
/// <summary> /// Make an STARTTLS command from the given enumerator. /// </summary> /// <param name="enumerator">The enumerator to create the command from.</param> /// <param name="command">The STARTTLS command that is defined within the token enumerator.</param> /// <param name="errorResponse">The error that indicates why the command could not be made.</param> /// <returns>Returns true if a command could be made, false if not.</returns> public bool TryMakeStartTls(TokenEnumerator enumerator, out SmtpCommand command, out SmtpResponse errorResponse) { Debug.Assert(enumerator.Peek() == new Token(TokenKind.Text, "STARTTLS")); command = null; errorResponse = null; enumerator.Take(); enumerator.TakeWhile(TokenKind.Space); if (enumerator.Count > 1) { _logger.LogVerbose("STARTTLS command can not have parameters (Tokens={0})", enumerator.Count); errorResponse = SmtpResponse.SyntaxError; return(false); } command = new StartTlsCommand(_options.ServerCertificate); return(true); }
/// <summary> /// Make a DATA command from the given enumerator. /// </summary> /// <param name="enumerator">The enumerator to create the command from.</param> /// <param name="command">The DATA command that is defined within the token enumerator.</param> /// <param name="errorResponse">The error that indicates why the command could not be made.</param> /// <returns>Returns true if a command could be made, false if not.</returns> public bool TryMakeData(TokenEnumerator enumerator, out SmtpCommand command, out SmtpResponse errorResponse) { Debug.Assert(enumerator.Peek() == new Token(TokenKind.Text, "DATA")); command = null; errorResponse = null; enumerator.Take(); enumerator.TakeWhile(TokenKind.Space); if (enumerator.Count > 1) { _logger.LogVerbose("DATA command can not have parameters (Tokens={0})", enumerator.Count); errorResponse = SmtpResponse.SyntaxError; return(false); } command = new DataCommand(_options.MessageStoreFactory); return(true); }
/// <summary> /// Try to make a quoted-string from the tokens. /// </summary> /// <param name="enumerator">The enumerator to make the quoted-string from.</param> /// <param name="quotedString">The quoted-string that was made, or undefined if it was not made.</param> /// <returns>true if the quoted-string was made, false if not.</returns> /// <remarks><![CDATA[DQUOTE * QcontentSMTP DQUOTE]]></remarks> public bool TryMakeQuotedString(TokenEnumerator enumerator, out string quotedString) { quotedString = null; if (enumerator.Take() != new Token(TokenKind.Punctuation, "\"")) { return(false); } while (enumerator.Peek() != new Token(TokenKind.Punctuation, "\"")) { string text; if (TryMakeQContentSmtp(enumerator, out text) == false) { return(false); } quotedString += text; } return(enumerator.Take() == new Token(TokenKind.Punctuation, "\"")); }
/// <summary> /// Try to make a domain name. /// </summary> /// <param name="enumerator">The enumerator to make the domain name from.</param> /// <param name="domain">The domain name that was made, or undefined if it was not made.</param> /// <returns>true if the domain name was made, false if not.</returns> /// <remarks><![CDATA[sub-domain *("." sub-domain)]]></remarks> public bool TryMakeDomain(TokenEnumerator enumerator, out string domain) { if (TryMake(enumerator, TryMakeSubdomain, out domain) == false) { return(false); } while (enumerator.Peek() == new Token(TokenKind.Punctuation, ".")) { enumerator.Take(); string subdomain; if (TryMake(enumerator, TryMakeSubdomain, out subdomain) == false) { return(false); } domain += String.Concat(".", subdomain); } return(true); }
/// <summary> /// Make a HELO command from the given enumerator. /// </summary> /// <param name="enumerator">The enumerator to create the command from.</param> /// <param name="command">The HELO command that is defined within the token enumerator.</param> /// <param name="errorResponse">The error that indicates why the command could not be made.</param> /// <returns>Returns true if a command could be made, false if not.</returns> public bool TryMakeHelo(TokenEnumerator enumerator, out SmtpCommand command, out SmtpResponse errorResponse) { Debug.Assert(enumerator.Peek() == new Token(TokenKind.Text, "HELO")); command = null; errorResponse = null; enumerator.Take(); enumerator.TakeWhile(TokenKind.Space); string domain; if (_parser.TryMakeDomain(enumerator, out domain) == false) { _logger.LogVerbose("Could not match the domain name (Text={0}).", enumerator.AsText()); errorResponse = SmtpResponse.SyntaxError; return(false); } command = new HeloCommand(domain); return(true); }
/// <summary> /// Try to make an @domain list. /// </summary> /// <param name="enumerator">The enumerator to make the @domain list from.</param> /// <param name="atDomainList">The @domain list that was made, or undefined if it was not made.</param> /// <returns>true if the @domain list was made, false if not.</returns> /// <remarks><![CDATA[At-domain *( "," At-domain )]]></remarks> public bool TryMakeAtDomainList(TokenEnumerator enumerator, out string atDomainList) { if (TryMake(enumerator, TryMakeAtDomain, out atDomainList) == false) { return(false); } // match the optional list while (enumerator.Peek() == new Token(TokenKind.Punctuation, ",")) { enumerator.Take(); string atDomain; if (TryMake(enumerator, TryMakeAtDomain, out atDomain) == false) { return(false); } atDomainList += String.Format(",{0}", atDomain); } return(true); }
/// <summary> /// Try to make a dot-string from the tokens. /// </summary> /// <param name="enumerator">The enumerator to make the dot-string from.</param> /// <param name="dotString">The dot-string that was made, or undefined if it was not made.</param> /// <returns>true if the dot-string was made, false if not.</returns> /// <remarks><![CDATA[Atom *("." Atom)]]></remarks> public bool TryMakeDotString(TokenEnumerator enumerator, out string dotString) { if (TryMake(enumerator, TryMakeAtom, out dotString) == false) { return(false); } while (enumerator.Peek() == new Token(TokenKind.Punctuation, ".")) { // skip the punctuation enumerator.Take(); string atom; if (TryMake(enumerator, TryMakeAtom, out atom) == false) { return(true); } dotString += String.Concat(".", atom); } return(true); }