267 lines
8.5 KiB
C#
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
|
|
}
|
|
} |