namespace Unosquare.Swan.Networking { using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; /// /// Represents an SMTP server response object. /// public class SmtpServerReply { #region Constructors /// /// Initializes a new instance of the class. /// /// The response code. /// The status code. /// The content. public SmtpServerReply(int responseCode, string statusCode, params string[] content) { Content = new List(); ReplyCode = responseCode; EnhancedStatusCode = statusCode; Content.AddRange(content); IsValid = responseCode >= 200 && responseCode < 600; ReplyCodeSeverity = SmtpReplyCodeSeverities.Unknown; ReplyCodeCategory = SmtpReplyCodeCategories.Unknown; if (!IsValid) return; if (responseCode >= 200) ReplyCodeSeverity = SmtpReplyCodeSeverities.PositiveCompletion; if (responseCode >= 300) ReplyCodeSeverity = SmtpReplyCodeSeverities.PositiveIntermediate; if (responseCode >= 400) ReplyCodeSeverity = SmtpReplyCodeSeverities.TransientNegative; if (responseCode >= 500) ReplyCodeSeverity = SmtpReplyCodeSeverities.PermanentNegative; if (responseCode >= 600) ReplyCodeSeverity = SmtpReplyCodeSeverities.Unknown; if (int.TryParse(responseCode.ToString(CultureInfo.InvariantCulture).Substring(1, 1), out var middleDigit)) { if (middleDigit >= 0 && middleDigit <= 5) ReplyCodeCategory = (SmtpReplyCodeCategories) middleDigit; } } /// /// Initializes a new instance of the class. /// public SmtpServerReply() : this(0, string.Empty, string.Empty) { // placeholder } /// /// Initializes a new instance of the class. /// /// The response code. /// The status code. /// The content. public SmtpServerReply(int responseCode, string statusCode, string content) : this(responseCode, statusCode, new[] {content}) { } /// /// Initializes a new instance of the class. /// /// The response code. /// The content. public SmtpServerReply(int responseCode, string content) : this(responseCode, string.Empty, content) { } #endregion #region Pre-built responses (https://tools.ietf.org/html/rfc5321#section-4.2.2) /// /// Gets the command unrecognized reply. /// public static SmtpServerReply CommandUnrecognized => new SmtpServerReply(500, "Syntax error, command unrecognized"); /// /// Gets the syntax error arguments reply. /// public static SmtpServerReply SyntaxErrorArguments => new SmtpServerReply(501, "Syntax error in parameters or arguments"); /// /// Gets the command not implemented reply. /// public static SmtpServerReply CommandNotImplemented => new SmtpServerReply(502, "Command not implemented"); /// /// Gets the bad sequence of commands reply. /// public static SmtpServerReply BadSequenceOfCommands => new SmtpServerReply(503, "Bad sequence of commands"); /// /// Gets the protocol violation reply. /// = public static SmtpServerReply ProtocolViolation => new SmtpServerReply(451, "Requested action aborted: error in processing"); /// /// Gets the system status bye reply. /// public static SmtpServerReply SystemStatusBye => new SmtpServerReply(221, "Service closing transmission channel"); /// /// Gets the system status help reply. /// = public static SmtpServerReply SystemStatusHelp => new SmtpServerReply(221, "Refer to RFC 5321"); /// /// Gets the bad syntax command empty reply. /// public static SmtpServerReply BadSyntaxCommandEmpty => new SmtpServerReply(400, "Error: bad syntax"); /// /// Gets the OK reply. /// public static SmtpServerReply Ok => new SmtpServerReply(250, "OK"); /// /// Gets the authorization required reply. /// public static SmtpServerReply AuthorizationRequired => new SmtpServerReply(530, "Authorization Required"); #endregion #region Properties /// /// Gets the response severity. /// public SmtpReplyCodeSeverities ReplyCodeSeverity { get; } /// /// Gets the response category. /// public SmtpReplyCodeCategories ReplyCodeCategory { get; } /// /// Gets the numeric response code. /// public int ReplyCode { get; } /// /// Gets the enhanced status code. /// public string EnhancedStatusCode { get; } /// /// Gets the content. /// public List Content { get; } /// /// Returns true if the response code is between 200 and 599. /// public bool IsValid { get; } /// /// Gets a value indicating whether this instance is positive. /// public bool IsPositive => ReplyCode >= 200 && ReplyCode <= 399; #endregion #region Methods /// /// Parses the specified text into a Server Reply for thorough analysis. /// /// The text. /// A new instance of SMTP server response object. public static SmtpServerReply Parse(string text) { var lines = text.Split(new[] {"\r\n"}, StringSplitOptions.RemoveEmptyEntries); if (lines.Length == 0) return new SmtpServerReply(); var lastLineParts = lines.Last().Split(new[] {" "}, StringSplitOptions.RemoveEmptyEntries); var enhancedStatusCode = string.Empty; int.TryParse(lastLineParts[0], out var responseCode); if (lastLineParts.Length > 1) { if (lastLineParts[1].Split('.').Length == 3) enhancedStatusCode = lastLineParts[1]; } var content = new List(); for (var i = 0; i < lines.Length; i++) { var splitChar = i == lines.Length - 1 ? " " : "-"; var lineParts = lines[i].Split(new[] {splitChar}, 2, StringSplitOptions.None); var lineContent = lineParts.Last(); if (string.IsNullOrWhiteSpace(enhancedStatusCode) == false) lineContent = lineContent.Replace(enhancedStatusCode, string.Empty).Trim(); content.Add(lineContent); } return new SmtpServerReply(responseCode, enhancedStatusCode, content.ToArray()); } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { var responseCodeText = ReplyCode.ToString(CultureInfo.InvariantCulture); var statusCodeText = string.IsNullOrWhiteSpace(EnhancedStatusCode) ? string.Empty : $" {EnhancedStatusCode.Trim()}"; if (Content.Count == 0) return $"{responseCodeText}{statusCodeText}"; var builder = new StringBuilder(); for (var i = 0; i < Content.Count; i++) { var isLastLine = i == Content.Count - 1; builder.Append(isLastLine ? $"{responseCodeText}{statusCodeText} {Content[i]}" : $"{responseCodeText}-{Content[i]}\r\n"); } return builder.ToString(); } #endregion } }