Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 66 additions & 32 deletions AegisImplictMail/MimeAttachment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.IO;
using System.Net.Mail;
using System.Net.Mime;
using System.Text;

namespace AegisImplicitMail
{
Expand All @@ -16,9 +17,10 @@ public class MimeAttachment : Attachment
internal static int AttachCount;
private AttachmentLocation _location;
/// <summary>
/// File to send.
/// Attachment is a File Stream
/// </summary>
public string FileName { get; set; }
public bool isFileStream { get; private set; } = false;

/// <summary>
/// Content type of file (application/octet-stream for regular attachments).
/// </summary>
Expand Down Expand Up @@ -48,74 +50,106 @@ public AttachmentLocation Location
}
}

public string GetEncodedAttachmentName()
{
Encoding displayNameEncoding = this.NameEncoding ?? Encoding.UTF8;
if (displayNameEncoding.Equals(Encoding.ASCII))
{
return this.ContentType.Name;
}
else
{
string encodingName = displayNameEncoding.BodyName.ToLower();
string encodedName = $"=?{encodingName}?B?{Convert.ToBase64String(displayNameEncoding.GetBytes(this.ContentType.Name))}?=";
return encodedName;
}
}

#endregion

#region Constructors
/// <summary>
/// Default constructor.
/// </summary>
// public SmtpAttachment():base(){}

/// <summary>
/// Constructor for passing all information at once.
/// Initializes a new instance of the AegisImplicitMail.MimeAttachment class with the specified
/// content string.
/// </summary>
/// <param name="fileName">File to send.</param>
/// <param name="contentType">Content type of file (application/octet-stream for regular attachments.)</param>
/// <param name="location">Where to put the attachment.</param>
public MimeAttachment(string fileName, ContentType contentType, AttachmentLocation location)
/// <param name="fileName">A System.String that contains a file path to use to create this attachment</param>
/// <param name="location">Attached or Inline</param>
public MimeAttachment(string fileName, AttachmentLocation location = AttachmentLocation.Attachmed)
: base(fileName)
{
if (string.IsNullOrEmpty(fileName))
{
throw new ArgumentException(nameof(fileName));
}
FileName = fileName;
ContentType = contentType;
Location = location;
}

/// <summary>
/// Initializes a new instance of the AegisImplicitMail.MimeAttachment class with the specified
/// content string and System.Net.Mime.ContentType.
/// </summary>
/// <param name="fileName">A System.String that contains a file path to use to create this attachment.</param>
/// <param name="contentType">A System.Net.Mime.ContentType that describes the data in string.</param>
/// <param name="location">Attached or Inline</param>
public MimeAttachment(string fileName, ContentType contentType, AttachmentLocation location = AttachmentLocation.Attachmed)
: base(fileName, contentType)
{
Location = location;
}

/// <summary>
/// Shortcut constructor for passing regular style attachments.
/// Initializes a new instance of the AegisImplicitMail.MimeAttachment class with the specified
/// stream and name.
/// </summary>
/// <param name="filename">File to send.</param>
public MimeAttachment(string filename)
: this(filename, new ContentType(MediaTypeNames.Application.Octet), AttachmentLocation.Attachmed)
/// <param name="contentStream">A readable System.IO.Stream that contains the content for this attachment.</param>
/// <param name="name">
/// A System.String that contains the value for the System.Net.Mime.ContentType.Name
/// property of the System.Net.Mime.ContentType associated with this attachment.
/// To ensure compatibility with the most number of email providers, this value cannot be null.
/// </param>
/// <param name="location">Attached or Inline</param>
public MimeAttachment(Stream contentStream, string name, AttachmentLocation location = AttachmentLocation.Attachmed)
: base(contentStream, name)
{
isFileStream = true;
Location = location;
}

/// <summary>
/// Constructor for passing stream attachments.
/// Initializes a new instance of the AegisImplicitMail.MimeAttachment class with the specified
/// stream and content type.
/// </summary>
/// <param name="contentStream">Stream to the attachment contents</param>
/// <param name="name">Name of the attachment as it will appear on the e-mail</param>
/// <param name="contentStream">Where to put the attachment</param>
public MimeAttachment(Stream contentStream, string name, AttachmentLocation location = AttachmentLocation.Attachmed)
:this(contentStream, name, new ContentType(MediaTypeNames.Application.Octet), location)
/// <param name="contentStream"> A readable System.IO.Stream that contains the content for this attachment.</param>
/// <param name="contentType">A System.Net.Mime.ContentType that describes the data in stream.</param>
/// <param name="location">Attached or Inline</param>
public MimeAttachment(Stream contentStream, ContentType contentType, AttachmentLocation location = AttachmentLocation.Attachmed)
:base(contentStream, contentType)
{
isFileStream = true;
Location = location;
}

/// <summary>
/// Constructor for passing stream attachments.
/// This constructor has no equivalent in System.Net.Mail.Attachment constructors.
/// Stays here for compatibility reasons.
/// </summary>
/// <param name="contentStream">Stream to the attachment contents</param>
/// <param name="name">Name of the attachment as it will appear on the e-mail</param>
/// <param name="contentType">Content type of the attachment</param>
/// <param name="contentStream">Where to put the attachment</param>
/// <param name="location">Attached or Inline</param>
public MimeAttachment(Stream contentStream, string name, ContentType contentType, AttachmentLocation location = AttachmentLocation.Attachmed)
: base(contentStream, name)
: this(contentStream, name)
{
isFileStream = true;
Location = location;
ContentType = contentType;
ContentType.Name = name;
}

/// <summary>
/// Show this attachment.
/// </summary>
/// <returns>The file name of the attachment.</returns>
/// <returns>The name of the attachment.</returns>
public override string ToString()
{
return FileName;
return this.ContentType.Name;
}

}
Expand Down
27 changes: 4 additions & 23 deletions AegisImplictMail/SmtpSocketClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1092,38 +1092,19 @@ private void SendAttachments(StringBuilder buf, AttachmentLocation type)
continue;
}

Stream stream;
string fileName;
if (string.IsNullOrEmpty(attachment.FileName))
{
stream = attachment.ContentStream;
fileName = attachment.Name;
}
else
{
stream = new FileStream(attachment.FileName, FileMode.Open, FileAccess.Read, FileShare.Read);
fileName = Path.GetFileName(attachment.FileName);
}

var cs = new CryptoStream(stream, new ToBase64Transform(), CryptoStreamMode.Read);
var cs = new CryptoStream(attachment.ContentStream, new ToBase64Transform(), CryptoStreamMode.Read);
_con.SendCommand(seperator);
var escapedFileName = fileName.Replace(@"\", @"\\").Replace(@"""", @"\""");
buf.Append("Content-Type: ");
buf.Append(attachment.ContentType);
buf.Append("; name=\"");
buf.Append(escapedFileName);
buf.Append("\"");
_con.SendCommand(buf.ToString());
_con.SendCommand("Content-Transfer-Encoding: base64");
buf.Length = 0;
buf.Append("Content-Disposition: attachment; filename=\"");
buf.Append(escapedFileName);
buf.Append("\"");
buf.Append("Content-Disposition: attachment; filename=");
buf.Append(attachment.GetEncodedAttachmentName());
_con.SendCommand(buf.ToString());
buf.Length = 0;
buf.Append("Content-ID: ");
var escapedContentId = "<" + (!string.IsNullOrEmpty(attachment.ContentId) ? attachment.ContentId : Path.GetFileNameWithoutExtension(fileName).Replace(" ", "-")) + ">";
buf.Append(escapedContentId);
buf.Append(attachment.ContentId);
buf.Append("\r\n");
_con.SendCommand(buf.ToString());
buf.Length = 0;
Expand Down