File: System\Net\Mail\Attachment.cs
Web Access
Project: src\src\libraries\System.Net.Mail\src\System.Net.Mail.csproj (System.Net.Mail)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net.Mime;
using System.Text;
 
namespace System.Net.Mail
{
    public abstract class AttachmentBase : IDisposable
    {
        internal bool disposed;
        private readonly MimePart _part = new MimePart();
 
        internal AttachmentBase()
        {
        }
 
        protected AttachmentBase(string fileName)
        {
            SetContentFromFile(fileName, string.Empty);
        }
 
        protected AttachmentBase(string fileName, string? mediaType)
        {
            SetContentFromFile(fileName, mediaType);
        }
 
        protected AttachmentBase(string fileName, ContentType? contentType)
        {
            SetContentFromFile(fileName, contentType);
        }
 
        protected AttachmentBase(Stream contentStream)
        {
            _part.SetContent(contentStream);
        }
 
        protected AttachmentBase(Stream contentStream, string? mediaType)
        {
            _part.SetContent(contentStream, null, mediaType);
        }
 
        internal AttachmentBase(Stream contentStream, string? name, string? mediaType)
        {
            _part.SetContent(contentStream, name, mediaType);
        }
 
        protected AttachmentBase(Stream contentStream, ContentType? contentType)
        {
            _part.SetContent(contentStream, contentType);
        }
 
        public void Dispose()
        {
            Dispose(true);
        }
 
        protected virtual void Dispose(bool disposing)
        {
            if (disposing && !disposed)
            {
                disposed = true;
                _part.Dispose();
            }
        }
 
        internal void SetContentFromFile(string fileName, ContentType? contentType)
        {
            ArgumentException.ThrowIfNullOrEmpty(fileName);
 
            Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
            _part.SetContent(stream, contentType);
        }
 
        internal void SetContentFromFile(string fileName, string? mediaType)
        {
            ArgumentException.ThrowIfNullOrEmpty(fileName);
 
            Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
            _part.SetContent(stream, null, mediaType);
        }
 
        internal void SetContentFromString(string content, ContentType? contentType)
        {
            ArgumentNullException.ThrowIfNull(content);
 
            _part.Stream?.Close();
 
            Encoding encoding;
 
            if (contentType != null && contentType.CharSet != null)
            {
                encoding = Text.Encoding.GetEncoding(contentType.CharSet);
            }
            else
            {
                if (MimeBasePart.IsAscii(content, false))
                {
                    encoding = Text.Encoding.ASCII;
                }
                else
                {
                    encoding = Text.Encoding.GetEncoding(MimeBasePart.DefaultCharSet);
                }
            }
            byte[] buffer = encoding.GetBytes(content);
            _part.SetContent(new MemoryStream(buffer), contentType);
 
            if (MimeBasePart.ShouldUseBase64Encoding(encoding))
            {
                _part.TransferEncoding = TransferEncoding.Base64;
            }
            else
            {
                _part.TransferEncoding = TransferEncoding.QuotedPrintable;
            }
        }
 
        internal void SetContentFromString(string content, Encoding? encoding, string? mediaType)
        {
            ArgumentNullException.ThrowIfNull(content);
 
            _part.Stream?.Close();
 
            if (string.IsNullOrEmpty(mediaType))
            {
                mediaType = MediaTypeNames.Text.Plain;
            }
 
            //validate the mediaType
            int offset = 0;
            try
            {
                string value = MailBnfHelper.ReadToken(mediaType, ref offset);
                if (value.Length == 0 || offset >= mediaType.Length || mediaType[offset++] != '/')
                    throw new ArgumentException(SR.MediaTypeInvalid, nameof(mediaType));
                value = MailBnfHelper.ReadToken(mediaType, ref offset);
                if (value.Length == 0 || offset < mediaType.Length)
                {
                    throw new ArgumentException(SR.MediaTypeInvalid, nameof(mediaType));
                }
            }
            catch (FormatException)
            {
                throw new ArgumentException(SR.MediaTypeInvalid, nameof(mediaType));
            }
 
 
            ContentType contentType = new ContentType(mediaType);
 
            if (encoding == null)
            {
                if (MimeBasePart.IsAscii(content, false))
                {
                    encoding = Encoding.ASCII;
                }
                else
                {
                    encoding = Encoding.GetEncoding(MimeBasePart.DefaultCharSet);
                }
            }
 
            contentType.CharSet = encoding.BodyName;
            byte[] buffer = encoding.GetBytes(content);
            _part.SetContent(new MemoryStream(buffer), contentType);
 
            if (MimeBasePart.ShouldUseBase64Encoding(encoding))
            {
                _part.TransferEncoding = TransferEncoding.Base64;
            }
            else
            {
                _part.TransferEncoding = TransferEncoding.QuotedPrintable;
            }
        }
 
 
        internal virtual void PrepareForSending(bool allowUnicode)
        {
            _part.ResetStream();
        }
 
        public Stream ContentStream
        {
            get
            {
                ObjectDisposedException.ThrowIf(disposed, this);
 
                return _part.Stream!;
            }
        }
 
        [AllowNull]
        public string ContentId
        {
            get
            {
                string? cid = _part.ContentID;
                if (string.IsNullOrEmpty(cid))
                {
                    cid = Guid.NewGuid().ToString();
                    ContentId = cid;
                    return cid;
                }
                if (cid.StartsWith('<') && cid.EndsWith('>'))
                {
                    return cid.Substring(1, cid.Length - 2);
                }
                return cid;
            }
 
            set
            {
                if (string.IsNullOrEmpty(value))
                {
                    _part.ContentID = null;
                }
                else
                {
                    if (value.AsSpan().ContainsAny('<', '>')) // invalid chars
                    {
                        throw new ArgumentException(SR.MailHeaderInvalidCID, nameof(value));
                    }
 
                    _part.ContentID = "<" + value + ">";
                }
            }
        }
 
        public ContentType ContentType
        {
            get
            {
                return _part.ContentType;
            }
 
            set
            {
                _part.ContentType = value;
            }
        }
 
        public TransferEncoding TransferEncoding
        {
            get
            {
                return _part.TransferEncoding;
            }
            set
            {
                _part.TransferEncoding = value;
            }
        }
 
        internal Uri? ContentLocation
        {
            get
            {
                Uri? uri;
                if (!Uri.TryCreate(_part.ContentLocation, UriKind.RelativeOrAbsolute, out uri))
                {
                    return null;
                }
                return uri;
            }
 
            set
            {
                _part.ContentLocation = value == null ? null : value.IsAbsoluteUri ? value.AbsoluteUri : value.OriginalString;
            }
        }
 
        internal MimePart MimePart
        {
            get
            {
                return _part;
            }
        }
    }
 
    public class Attachment : AttachmentBase
    {
        private string? _name;
        private Encoding? _nameEncoding;
 
        internal Attachment()
        {
            MimePart.ContentDisposition = new ContentDisposition();
        }
 
        public Attachment(string fileName) : base(fileName)
        {
            Name = Path.GetFileName(fileName);
            MimePart.ContentDisposition = new ContentDisposition();
        }
 
        public Attachment(string fileName, string? mediaType) :
            base(fileName, mediaType)
        {
            Name = Path.GetFileName(fileName);
            MimePart.ContentDisposition = new ContentDisposition();
        }
 
        public Attachment(string fileName, ContentType contentType) :
            base(fileName, contentType)
        {
            if (string.IsNullOrEmpty(contentType.Name))
            {
                Name = Path.GetFileName(fileName);
            }
            else
            {
                Name = contentType.Name;
            }
            MimePart.ContentDisposition = new ContentDisposition();
        }
 
        public Attachment(Stream contentStream, string? name) :
            base(contentStream, null, null)
        {
            Name = name;
            MimePart.ContentDisposition = new ContentDisposition();
        }
 
        public Attachment(Stream contentStream, string? name, string? mediaType) :
            base(contentStream, null, mediaType)
        {
            Name = name;
            MimePart.ContentDisposition = new ContentDisposition();
        }
 
        public Attachment(Stream contentStream, ContentType contentType) :
            base(contentStream, contentType)
        {
            Name = contentType.Name;
            MimePart.ContentDisposition = new ContentDisposition();
        }
 
        internal void SetContentTypeName(bool allowUnicode)
        {
            if (!allowUnicode && !string.IsNullOrEmpty(_name) && !MimeBasePart.IsAscii(_name, false))
            {
                Encoding encoding = NameEncoding ?? Encoding.GetEncoding(MimeBasePart.DefaultCharSet);
                MimePart.ContentType.Name = MimeBasePart.EncodeHeaderValue(_name, encoding, MimeBasePart.ShouldUseBase64Encoding(encoding));
            }
            else
            {
                MimePart.ContentType.Name = _name;
            }
        }
 
        public string? Name
        {
            get
            {
                return _name;
            }
            set
            {
                Encoding? nameEncoding = MimeBasePart.DecodeEncoding(value);
                if (nameEncoding != null)
                {
                    _nameEncoding = nameEncoding;
                    _name = MimeBasePart.DecodeHeaderValue(value);
                    MimePart.ContentType.Name = value;
                }
                else
                {
                    _name = value;
                    SetContentTypeName(true);
                    // This keeps ContentType.Name up to date for user viewability, but isn't necessary.
                    // SetContentTypeName is called again by PrepareForSending()
                }
            }
        }
 
        public Encoding? NameEncoding
        {
            get
            {
                return _nameEncoding;
            }
            set
            {
                _nameEncoding = value;
                if (_name != null && _name != string.Empty)
                {
                    SetContentTypeName(true);
                }
            }
        }
 
        public ContentDisposition? ContentDisposition
        {
            get
            {
                return MimePart.ContentDisposition;
            }
        }
 
        internal override void PrepareForSending(bool allowUnicode)
        {
            if (_name != null && _name != string.Empty)
            {
                SetContentTypeName(allowUnicode);
            }
            base.PrepareForSending(allowUnicode);
        }
 
        public static Attachment CreateAttachmentFromString(string content, string? name)
        {
            Attachment a = new Attachment();
            a.SetContentFromString(content, null, string.Empty);
            a.Name = name;
            return a;
        }
 
        public static Attachment CreateAttachmentFromString(string content, string? name, Encoding? contentEncoding, string? mediaType)
        {
            Attachment a = new Attachment();
            a.SetContentFromString(content, contentEncoding, mediaType);
            a.Name = name;
            return a;
        }
 
        public static Attachment CreateAttachmentFromString(string content, ContentType contentType)
        {
            Attachment a = new Attachment();
            a.SetContentFromString(content, contentType);
            a.Name = contentType.Name;
            return a;
        }
    }
}