/// <summary> /// Execute the command handler against the specified session context. /// </summary> /// <param name="context">The session context to execute the command handler against.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> public virtual async Task ExecuteAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { var retryCount = _maxRetries; while (retryCount-- > 0 && context.IsQuitRequested == false && cancellationToken.IsCancellationRequested == false) { var text = await context.Text.ReadLineAsync(cancellationToken).ConfigureAwait(false); SmtpCommand command; SmtpResponse errorResponse; if (TryAccept(context, text, out command, out errorResponse) == false) { var response = new SmtpResponse(errorResponse.ReplyCode, $"{errorResponse.Message}, {retryCount} retry(ies) remaining."); await context.Text.ReplyAsync(response, cancellationToken); continue; } // the command was a normal command so we can reset the retry count retryCount = _maxRetries; await ExecuteAsync(command, context, cancellationToken).ConfigureAwait(false); } }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> public override async Task ExecuteAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { switch (_method) { case AuthenticationMethod.Plain: if (await TryPlainAsync(context, cancellationToken) == false) { await context.Text.ReplyAsync(SmtpResponse.AuthenticationFailed, cancellationToken).ConfigureAwait(false); return; } break; case AuthenticationMethod.Login: if (await TryLoginAsync(context, cancellationToken) == false) { await context.Text.ReplyAsync(SmtpResponse.AuthenticationFailed, cancellationToken).ConfigureAwait(false); return; } break; } if (await _userAuthenticator.AuthenticateAsync(_user, _password).ConfigureAwait(false) == false) { await context.Text.ReplyAsync(SmtpResponse.AuthenticationFailed, cancellationToken).ConfigureAwait(false); return; } await context.Text.ReplyAsync(SmtpResponse.AuthenticationSuccessful, cancellationToken).ConfigureAwait(false); context.StateMachine.RemoveCommand(SmtpState.WaitingForMail, "AUTH"); context.StateMachine.RemoveCommand(SmtpState.WaitingForMailSecure, "AUTH"); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> public override async Task ExecuteAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { switch (Method) { case AuthenticationMethod.Plain: if (await TryPlainAsync(context, cancellationToken) == false) { await context.Text.ReplyAsync(SmtpResponse.AuthenticationFailed, cancellationToken).ConfigureAwait(false); return; } break; case AuthenticationMethod.Login: if (await TryLoginAsync(context, cancellationToken) == false) { await context.Text.ReplyAsync(SmtpResponse.AuthenticationFailed, cancellationToken).ConfigureAwait(false); return; } break; } if (await _userAuthenticator.AuthenticateAsync(_user, _password).ConfigureAwait(false) == false) { await context.Text.ReplyAsync(SmtpResponse.AuthenticationFailed, cancellationToken).ConfigureAwait(false); return; } await context.Text.ReplyAsync(SmtpResponse.AuthenticationSuccessful, cancellationToken).ConfigureAwait(false); context.StateMachine.RemoveCommand(SmtpState.WaitingForMail, "AUTH"); context.StateMachine.RemoveCommand(SmtpState.WaitingForMailSecure, "AUTH"); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> public override async Task ExecuteAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { if (context.Transaction.To.Count == 0) { await context.Text.ReplyAsync(SmtpResponse.NoValidRecipientsGiven, cancellationToken).ConfigureAwait(false); return; } await context.Text.ReplyAsync(new SmtpResponse(SmtpReplyCode.StartMailInput, "end with <CRLF>.<CRLF>"), cancellationToken).ConfigureAwait(false); await ReceiveContentAsync(context, cancellationToken); try { // store the transaction using (var container = new DisposableContainer <IMessageStore>(_options.MessageStoreFactory.CreateInstance(context))) { var response = await container.Instance.SaveAsync(context, context.Transaction, cancellationToken).ConfigureAwait(false); await context.Text.ReplyAsync(response, cancellationToken).ConfigureAwait(false); } } catch (Exception) { await context.Text.ReplyAsync(new SmtpResponse(SmtpReplyCode.TransactionFailed), cancellationToken).ConfigureAwait(false); } }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> public override async Task ExecuteAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { using (var container = new DisposableContainer <IMailboxFilter>(_mailboxFilterFactory.CreateInstance(context))) { switch (await container.Instance.CanDeliverToAsync(context, _address, context.Transaction.From)) { case MailboxFilterResult.Yes: context.Transaction.To.Add(_address); await context.Text.ReplyAsync(SmtpResponse.Ok, cancellationToken); return; case MailboxFilterResult.NoTemporarily: await context.Text.ReplyAsync(SmtpResponse.MailboxUnavailable, cancellationToken); return; case MailboxFilterResult.NoPermanently: await context.Text.ReplyAsync(SmtpResponse.MailboxNameNotAllowed, cancellationToken); return; } } throw new NotSupportedException("The Acceptance state is not supported."); }
/// <summary> /// Returns a value indicating whether or not plain login is allowed. /// </summary> /// <param name="session">The current session.</param> /// <returns>true if plain login is allowed for the session, false if not.</returns> bool IsPlainLoginAllowed(ISmtpSessionContext session) { if (_options.UserAuthenticator == null) { return false; } return session.Text.IsSecure || _options.AllowUnsecureAuthentication; }
/// <summary> /// Returns a value indicating whether or not plain login is allowed. /// </summary> /// <param name="session">The current session.</param> /// <returns>true if plain login is allowed for the session, false if not.</returns> bool IsPlainLoginAllowed(ISmtpSessionContext session) { if (_options.UserAuthenticator == null) { return(false); } return(session.Text.IsSecure || _options.AllowUnsecureAuthentication); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> public override async Task ExecuteAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { await context.Text.ReplyAsync(SmtpResponse.ServiceReady, cancellationToken); var stream = new SslStream(context.Text.GetInnerStream(), true); await stream.AuthenticateAsServerAsync(_certificate, false, SslProtocols.Default, true); context.Text = new NetworkTextStream(stream); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> public override async Task ExecuteAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { var greeting = $"{_options.ServerName} Hello {DomainOrAddress}, haven't we met before?"; var output = new[] { greeting }.Union(GetExtensions(context)).ToArray(); for (var i = 0; i < output.Length - 1; i++) { await context.Text.WriteLineAsync($"250-{output[i]}", cancellationToken); } await context.Text.WriteLineAsync($"250 {output[output.Length - 1]}", cancellationToken); await context.Text.FlushAsync(cancellationToken); }
/// <summary> /// Receive the message content. /// </summary> /// <param name="context">The SMTP session context to receive the message within.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the operation.</returns> async Task ReceiveContentAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { if (context.TransferEncoding == ContentEncoding.EightBit || _options.DefaultContentEncoding == ContentEncoding.EightBit) { context.Text = new NetworkTextStream(context.Text.GetInnerStream(), Encoding.UTF8); } await ReceiveShortLineContentAsync(context, cancellationToken); if (context.TransferEncoding == ContentEncoding.EightBit || _options.DefaultContentEncoding == ContentEncoding.EightBit) { context.Text = new NetworkTextStream(context.Text.GetInnerStream(), Encoding.ASCII); } }
/// <summary> /// Attempt a LOGIN login sequence. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>true if the LOGIN login sequence worked, false if not.</returns> async Task <bool> TryLoginAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { await context.Text.ReplyAsync(new SmtpResponse(SmtpReplyCode.ContinueWithAuth, "VXNlcm5hbWU6"), cancellationToken); _user = Encoding.UTF8.GetString( Convert.FromBase64String( await context.Text.ReadLineAsync(cancellationToken).ConfigureAwait(false))); await context.Text.ReplyAsync(new SmtpResponse(SmtpReplyCode.ContinueWithAuth, "UGFzc3dvcmQ6"), cancellationToken); _password = Encoding.UTF8.GetString( Convert.FromBase64String( await context.Text.ReadLineAsync(cancellationToken).ConfigureAwait(false))); return(true); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> public override async Task ExecuteAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { context.Transaction.Reset(); // check if a size has been defined var size = GetMessageSize(); // check against the server supplied maximum if (_options.MaxMessageSize > 0 && size > _options.MaxMessageSize) { await context.Text.ReplyAsync(SmtpResponse.SizeLimitExceeded, cancellationToken); return; } using (var container = new DisposableContainer <IMailboxFilter>(_options.MailboxFilterFactory.CreateInstance(context))) { switch (await container.Instance.CanAcceptFromAsync(context, Address, size)) { case MailboxFilterResult.Yes: context.Transaction.From = Address; context.TransferEncoding = GetTransferEncoding() ?? context.TransferEncoding; await context.Text.ReplyAsync(SmtpResponse.Ok, cancellationToken); return; case MailboxFilterResult.NoTemporarily: await context.Text.ReplyAsync(SmtpResponse.MailboxUnavailable, cancellationToken); return; case MailboxFilterResult.NoPermanently: await context.Text.ReplyAsync(SmtpResponse.MailboxNameNotAllowed, cancellationToken); return; case MailboxFilterResult.SizeLimitExceeded: await context.Text.ReplyAsync(SmtpResponse.SizeLimitExceeded, cancellationToken); return; } } throw new NotSupportedException("The Acceptance state is not supported."); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> public override async Task ExecuteAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { if (context.Transaction.To.Count == 0) { await context.Text.ReplyAsync(SmtpResponse.NoValidRecipientsGiven, cancellationToken).ConfigureAwait(false); return; } await context.Text.ReplyAsync(new SmtpResponse(SmtpReplyCode.StartMailInput, "end with <CRLF>.<CRLF>"), cancellationToken).ConfigureAwait(false); try { string text; while ((text = await context.Text.ReadLineAsync(TimeSpan.FromSeconds(60), cancellationToken).ConfigureAwait(false)) != ".") { // need to trim the '.' at the start of the line if it // exists as this would have been added for transparency // http://tools.ietf.org/html/rfc5321#section-4.5.2 context.Transaction.Mime.AppendLine(text.TrimStart('.')); } } catch (TimeoutException) { // TODO: not sure what the best thing to do here is throw; } try { // store the transaction using (var container = new DisposableContainer <IMessageStore>(_messageStoreFactory.CreateInstance(context))) { var response = await container.Instance.SaveAsync(context, context.Transaction, cancellationToken).ConfigureAwait(false); await context.Text.ReplyAsync(response, cancellationToken).ConfigureAwait(false); } } catch (Exception) { await context.Text.ReplyAsync(new SmtpResponse(SmtpReplyCode.TransactionFailed), cancellationToken).ConfigureAwait(false); } }
/// <summary> /// Gets the list of extensions. /// </summary> /// <param name="session">The session the is currently operating.</param> /// <returns>The list of extensions that are allowed for the session.</returns> IEnumerable<string> GetExtensions(ISmtpSessionContext session) { yield return "PIPELINING"; if (session.Text.IsSecure == false && _options.ServerCertificate != null) { yield return "STARTTLS"; } if (_options.MaxMessageSize > 0) { yield return $"SIZE {_options.MaxMessageSize}"; } if (IsPlainLoginAllowed(session)) { yield return "AUTH PLAIN LOGIN"; } }
/// <summary> /// Gets the list of extensions. /// </summary> /// <param name="session">The session the is currently operating.</param> /// <returns>The list of extensions that are allowed for the session.</returns> IEnumerable <string> GetExtensions(ISmtpSessionContext session) { yield return("PIPELINING"); if (session.Text.IsSecure == false && _options.ServerCertificate != null) { yield return("STARTTLS"); } if (_options.MaxMessageSize > 0) { yield return($"SIZE {_options.MaxMessageSize}"); } if (IsPlainLoginAllowed(session)) { yield return("AUTH PLAIN LOGIN"); } }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> public override async Task ExecuteAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { if (context.Transaction.To.Count == 0) { await context.Text.ReplyAsync(SmtpResponse.NoValidRecipientsGiven, cancellationToken).ConfigureAwait(false); return; } await context.Text.ReplyAsync(new SmtpResponse(SmtpReplyCode.StartMailInput, "end with <CRLF>.<CRLF>"), cancellationToken).ConfigureAwait(false); try { string text; while ((text = await context.Text.ReadLineAsync(TimeSpan.FromSeconds(60), cancellationToken).ConfigureAwait(false)) != ".") { // need to trim the '.' at the start of the line if it // exists as this would have been added for transparency // http://tools.ietf.org/html/rfc5321#section-4.5.2 context.Transaction.Mime.AppendLine(text.TrimStart('.')); } } catch (TimeoutException) { // TODO: not sure what the best thing to do here is throw; } try { // store the transaction using (var container = new DisposableContainer<IMessageStore>(_messageStoreFactory.CreateInstance(context))) { var response = await container.Instance.SaveAsync(context, context.Transaction, cancellationToken).ConfigureAwait(false); await context.Text.ReplyAsync(response, cancellationToken).ConfigureAwait(false); } } catch (Exception) { await context.Text.ReplyAsync(new SmtpResponse(SmtpReplyCode.TransactionFailed), cancellationToken).ConfigureAwait(false); } }
/// <summary> /// Receive the message content in short line format. /// </summary> /// <param name="context">The SMTP session context to receive the message within.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the operation.</returns> async Task ReceiveShortLineContentAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { try { string text; while ((text = await context.Text.ReadLineAsync(TimeSpan.FromSeconds(60), cancellationToken).ConfigureAwait(false)) != ".") { // need to trim the '.' at the start of the line if it // exists as this would have been added for transparency // http://tools.ietf.org/html/rfc5321#section-4.5.2 var line = (!string.IsNullOrWhiteSpace(text) && text.Length > 1 && text.Substring(0, 1) == ".") ? text.Substring(1) : text; context.Transaction.Mime.AppendLine(line); } } catch (TimeoutException) { // TODO: not sure what the best thing to do here is throw; } }
/// <summary> /// Attempt a PLAIN login sequence. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>true if the PLAIN login sequence worked, false if not.</returns> async Task<bool> TryPlainAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { await context.Text.ReplyAsync(new SmtpResponse(SmtpReplyCode.ContinueWithAuth, " "), cancellationToken).ConfigureAwait(false); var authentication = Encoding.UTF8.GetString( Convert.FromBase64String( await context.Text.ReadLineAsync(cancellationToken).ConfigureAwait(false))); var match = Regex.Match(authentication, "\x0000(?<user>.*)\x0000(?<password>.*)"); if (match.Success == false) { await context.Text.ReplyAsync(SmtpResponse.AuthenticationFailed, cancellationToken).ConfigureAwait(false); return false; } _user = match.Groups["user"].Value; _password = match.Groups["password"].Value; return true; }
/// <summary> /// Attempt a PLAIN login sequence. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>true if the PLAIN login sequence worked, false if not.</returns> async Task <bool> TryPlainAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { await context.Text.ReplyAsync(new SmtpResponse(SmtpReplyCode.ContinueWithAuth, " "), cancellationToken).ConfigureAwait(false); var authentication = Encoding.UTF8.GetString( Convert.FromBase64String( await context.Text.ReadLineAsync(cancellationToken).ConfigureAwait(false))); var match = Regex.Match(authentication, "\x0000(?<user>.*)\x0000(?<password>.*)"); if (match.Success == false) { await context.Text.ReplyAsync(SmtpResponse.AuthenticationFailed, cancellationToken).ConfigureAwait(false); return(false); } _user = match.Groups["user"].Value; _password = match.Groups["password"].Value; return(true); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> public override async Task ExecuteAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { context.Transaction.Reset(); // check if a size has been defined var size = GetMessageSize(); // check against the server supplied maximum if (_maxMessageSize > 0 && size > _maxMessageSize) { await context.Text.ReplyAsync(SmtpResponse.SizeLimitExceeded, cancellationToken); return; } using (var container = new DisposableContainer<IMailboxFilter>(_mailboxFilterFactory.CreateInstance(context))) { switch (await container.Instance.CanAcceptFromAsync(context, Address, size)) { case MailboxFilterResult.Yes: context.Transaction.From = _address; await context.Text.ReplyAsync(SmtpResponse.Ok, cancellationToken); return; case MailboxFilterResult.NoTemporarily: await context.Text.ReplyAsync(SmtpResponse.MailboxUnavailable, cancellationToken); return; case MailboxFilterResult.NoPermanently: await context.Text.ReplyAsync(SmtpResponse.MailboxNameNotAllowed, cancellationToken); return; case MailboxFilterResult.SizeLimitExceeded: await context.Text.ReplyAsync(SmtpResponse.SizeLimitExceeded, cancellationToken); return; } } throw new NotSupportedException("The Acceptance state is not supported."); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> public override async Task ExecuteAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { using (var container = new DisposableContainer<IMailboxFilter>(_mailboxFilterFactory.CreateInstance(context))) { switch (await container.Instance.CanDeliverToAsync(context, _address, context.Transaction.From)) { case MailboxFilterResult.Yes: context.Transaction.To.Add(_address); await context.Text.ReplyAsync(SmtpResponse.Ok, cancellationToken); return; case MailboxFilterResult.NoTemporarily: await context.Text.ReplyAsync(SmtpResponse.MailboxUnavailable, cancellationToken); return; case MailboxFilterResult.NoPermanently: await context.Text.ReplyAsync(SmtpResponse.MailboxNameNotAllowed, cancellationToken); return; } } throw new NotSupportedException("The Acceptance state is not supported."); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> public override Task ExecuteAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { var response = new SmtpResponse(SmtpReplyCode.Ok, $"Hello {Domain}, haven't we met before?"); return context.Text.ReplyAsync(response, cancellationToken); }
/// <summary> /// Attempt a LOGIN login sequence. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>true if the LOGIN login sequence worked, false if not.</returns> async Task<bool> TryLoginAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { await context.Text.ReplyAsync(new SmtpResponse(SmtpReplyCode.ContinueWithAuth, "VXNlcm5hbWU6"), cancellationToken); _user = Encoding.UTF8.GetString( Convert.FromBase64String( await context.Text.ReadLineAsync(cancellationToken).ConfigureAwait(false))); await context.Text.ReplyAsync(new SmtpResponse(SmtpReplyCode.ContinueWithAuth, "UGFzc3dvcmQ6"), cancellationToken); _password = Encoding.UTF8.GetString( Convert.FromBase64String( await context.Text.ReadLineAsync(cancellationToken).ConfigureAwait(false))); return true; }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> public override Task ExecuteAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { return context.Text.ReplyAsync(SmtpResponse.Ok, cancellationToken); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> public override Task ExecuteAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { var response = new SmtpResponse(SmtpReplyCode.Ok, $"Hello {Domain}, haven't we met before?"); return(context.Text.ReplyAsync(response, cancellationToken)); }
/// <summary> /// Execute the command. /// </summary> /// <param name="command">The command to execute.</param> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> protected override Task ExecuteAsync(SmtpCommand command, ISmtpSessionContext context, CancellationToken cancellationToken) { _visitor.Visit(command); return(command.ExecuteAsync(context, cancellationToken)); }
/// <summary> /// Execute the command. /// </summary> /// <param name="command">The command to execute.</param> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> protected virtual Task ExecuteAsync(SmtpCommand command, ISmtpSessionContext context, CancellationToken cancellationToken) { return(command.ExecuteAsync(context, cancellationToken)); }
/// <summary> /// Advances the enumerator to the next command in the stream. /// </summary> /// <param name="context">The session context to execute the command handler against.</param> /// <param name="text">The text to return the commands from.</param> /// <param name="command">The command that was found.</param> /// <param name="errorResponse">The error response that indicates why a command could not be accepted.</param> /// <returns>true if a valid command was found, false if not.</returns> protected virtual bool TryAccept(ISmtpSessionContext context, string text, out SmtpCommand command, out SmtpResponse errorResponse) { return(context.StateMachine.TryAccept(new TokenEnumerator(new StringTokenReader(text)), out command, out errorResponse)); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> public override Task ExecuteAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { context.Quit(); return(context.Text.ReplyAsync(SmtpResponse.ServiceClosingTransmissionChannel, cancellationToken)); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> public abstract Task ExecuteAsync(ISmtpSessionContext context, CancellationToken cancellationToken);
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> public override Task ExecuteAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { context.Transaction.Reset(); return(context.Text.ReplyAsync(SmtpResponse.Ok, cancellationToken)); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> public override Task ExecuteAsync(ISmtpSessionContext context, CancellationToken cancellationToken) { context.Quit(); return context.Text.ReplyAsync(SmtpResponse.ServiceClosingTransmissionChannel, cancellationToken); }