RaspberryIO/Unosquare.Swan/Networking/SmtpServerReply.cs
2019-12-03 18:44:25 +01:00

267 lines
8.5 KiB
C#

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