using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using MailServer.Misc.IO;
namespace MailServer.Misc.MIME
{
///
/// Represents a MIME entity. Defined in RFC 2045 2.4.
///
public class MIME_Entity : IDisposable
{
private bool m_IsDisposed = false;
private MIME_Entity m_pParent = null;
private MIME_h_Collection m_pHeader = null;
private MIME_b m_pBody = null;
private MIME_b_Provider m_pBodyProvider = null;
///
/// Default constructor.
///
public MIME_Entity()
{
m_pHeader = new MIME_h_Collection(new MIME_h_Provider());
m_pBodyProvider = new MIME_b_Provider();
}
#region method Dispose
///
/// Cleans up any resources being used. This method is thread-safe.
///
public void Dispose()
{
lock(this){
if(m_IsDisposed){
return;
}
m_IsDisposed = true;
m_pHeader = null;
m_pParent = null;
}
}
#endregion
#region method ToFile
///
/// Stores MIME entity to the specified file.
///
/// File name with path where to store MIME entity.
/// Header 8-bit words ecnoder. Value null means that words are not encoded.
/// Charset to use to encode 8-bit header parameters. Value null means parameters not encoded.
/// Is raised when file is null.
/// Is raised when any of the arguments has invalid value.
public void ToFile(string file,MIME_Encoding_EncodedWord headerWordEncoder,Encoding headerParmetersCharset)
{
if(file == null){
throw new ArgumentNullException("file");
}
if(file == ""){
throw new ArgumentException("Argument 'file' value must be specified.");
}
using(FileStream fs = File.Create(file)){
ToStream(fs,headerWordEncoder,headerParmetersCharset);
}
}
#endregion
#region method ToStream
///
/// Store MIME enity to the specified stream.
///
/// Stream where to store MIME entity. Storing starts form stream current position.
/// Header 8-bit words ecnoder. Value null means that words are not encoded.
/// Charset to use to encode 8-bit header parameters. Value null means parameters not encoded.
/// Is raised when stream is null.
public void ToStream(Stream stream,MIME_Encoding_EncodedWord headerWordEncoder,Encoding headerParmetersCharset)
{
if(stream == null){
throw new ArgumentNullException("stream");
}
m_pHeader.ToStream(stream,headerWordEncoder,headerParmetersCharset);
stream.Write(new byte[]{(int)'\r',(int)'\n'},0,2);
m_pBody.ToStream(stream,headerWordEncoder,headerParmetersCharset);
}
#endregion
#region method ToString
///
/// Returns MIME entity as string.
///
/// Returns MIME entity as string.
public override string ToString()
{
return ToString(null,null);
}
///
/// Returns MIME entity as string.
///
/// Header 8-bit words ecnoder. Value null means that words are not encoded.
/// Charset to use to encode 8-bit header parameters. Value null means parameters not encoded.
/// Returns MIME entity as string.
public string ToString(MIME_Encoding_EncodedWord headerWordEncoder,Encoding headerParmetersCharset)
{
using(MemoryStream ms = new MemoryStream()){
ToStream(ms,headerWordEncoder,headerParmetersCharset);
return Encoding.UTF8.GetString(ms.ToArray());
}
}
#endregion
#region method ToByte
///
/// Returns MIME entity as byte[].
///
/// Header 8-bit words ecnoder. Value null means that words are not encoded.
/// Charset to use to encode 8-bit header parameters. Value null means parameters not encoded.
/// Returns MIME entity as byte[].
public byte[] ToByte(MIME_Encoding_EncodedWord headerWordEncoder,Encoding headerParmetersCharset)
{
using(MemoryStream ms = new MemoryStream()){
ToStream(ms,headerWordEncoder,headerParmetersCharset);
return ms.ToArray();
}
}
#endregion
#region method Parse
///
/// Parses MIME entiry from the specified stream.
///
/// Source stream.
/// Default content type.
/// Is raised when stream or defaultContentType is null reference.
internal void Parse(SmartStream stream,MIME_h_ContentType defaultContentType)
{
if(stream == null){
throw new ArgumentNullException("stream");
}
if(defaultContentType == null){
throw new ArgumentNullException("defaultContentType");
}
m_pHeader.Parse(stream);
m_pBody = m_pBodyProvider.Parse(this,stream,defaultContentType);
m_pBody.SetParent(this,false);
}
#endregion
#region method SetParent
///
/// Sets MIME entity parent entity.
///
/// Parent entity.
internal void SetParent(MIME_Entity parent)
{
m_pParent = parent;
}
#endregion
#region Properties Implementation
// Permanent headerds list: http://www.rfc-editor.org/rfc/rfc4021.txt
///
/// Gets if this object is disposed.
///
public bool IsDisposed
{
get{ return m_IsDisposed; }
}
///
/// Gets if this entity is modified since it has loaded.
///
/// Is riased when this class is disposed and this property is accessed.
public bool IsModified
{
get{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
return m_pHeader.IsModified || m_pBody.IsModified;
}
}
///
/// Gets the parent entity of this entity, returns null if this is the root entity.
///
/// Is raised when this object is disposed and this property is accessed.
public MIME_Entity Parent
{
get{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
return m_pParent;
}
}
///
/// Gets MIME entity header field collection.
///
/// Is raised when this object is disposed and this property is accessed.
public MIME_h_Collection Header
{
get{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
return m_pHeader;
}
}
///
/// Gets or sets MIME version number. Value null means that header field does not exist. Normally this value is 1.0. Defined in RFC 2045 section 4.
///
/// Is raised when this object is disposed and this property is accessed.
/// An indicator that this message is formatted according to the MIME
/// standard, and an indication of which version of MIME is used.
public string MimeVersion
{
get{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
MIME_h h = m_pHeader.GetFirst("MIME-Version");
if(h != null){
return ((MIME_h_Unstructured)h).Value;
}
else{
return null;
}
}
set{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
if(value == null){
m_pHeader.RemoveAll("MIME-Version");
}
else{
MIME_h h = m_pHeader.GetFirst("MIME-Version");
if(h == null){
h = new MIME_h_Unstructured("MIME-Version",value);
m_pHeader.Add(h);
}
else{
((MIME_h_Unstructured)h).Value = value;
}
}
}
}
///
/// Gets or sets content body part ID. Value null means that header field does not exist. Defined in RFC 2045 7.
///
/// Is raised when this object is disposed and this property is accessed.
/// Specifies a Unique ID for one MIME body part of the content of a message.
public string ContentID
{
get{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
MIME_h h = m_pHeader.GetFirst("Content-ID");
if(h != null){
return ((MIME_h_Unstructured)h).Value;
}
else{
return null;
}
}
set{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
if(value == null){
m_pHeader.RemoveAll("Content-ID");
}
else{
MIME_h h = m_pHeader.GetFirst("Content-ID");
if(h == null){
h = new MIME_h_Unstructured("Content-ID",value);
m_pHeader.Add(h);
}
else{
((MIME_h_Unstructured)h).Value = value;
}
}
}
}
///
/// Gets or sets description of message body part. Value null means that header field does not exist. Defined in RFC 2045 8.
///
/// Is raised when this object is disposed and this property is accessed.
/// Description of a particular body part of a message; for example, a caption for an image body part.
public string ContentDescription
{
get{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
MIME_h h = m_pHeader.GetFirst("Content-Description");
if(h != null){
return ((MIME_h_Unstructured)h).Value;
}
else{
return null;
}
}
set{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
if(value == null){
m_pHeader.RemoveAll("Content-Description");
}
else{
MIME_h h = m_pHeader.GetFirst("Content-Description");
if(h == null){
h = new MIME_h_Unstructured("Content-Description",value);
m_pHeader.Add(h);
}
else{
((MIME_h_Unstructured)h).Value = value;
}
}
}
}
///
/// Gets or sets content transfer encoding. Value null means that header field does not exist.
/// RFC defined values are in MIME_TransferEncodings. Defined in RFC 2045 6.
///
/// Is raised when this object is disposed and this property is accessed.
/// Coding method used in a MIME message body part.
public string ContentTransferEncoding
{
get{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
MIME_h h = m_pHeader.GetFirst("Content-Transfer-Encoding");
if(h != null){
return ((MIME_h_Unstructured)h).Value;
}
else{
return null;
}
}
set{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
if(value == null){
m_pHeader.RemoveAll("Content-Transfer-Encoding");
}
else{
MIME_h h = m_pHeader.GetFirst("Content-Transfer-Encoding");
if(h == null){
h = new MIME_h_Unstructured("Content-Transfer-Encoding",value);
m_pHeader.Add(h);
}
else{
((MIME_h_Unstructured)h).Value = value;
}
}
}
}
///
/// Gets or sets MIME content type. Value null means that header field does not exist. Defined in RFC 2045 5.
///
/// Is raised when this object is disposed and this property is accessed.
/// Is raised when header field parsing errors.
public MIME_h_ContentType ContentType
{
get{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
MIME_h h = m_pHeader.GetFirst("Content-Type");
if(h != null){
if(!(h is MIME_h_ContentType)){
throw new ParseException("Header field 'ContentType' parsing failed.");
}
return (MIME_h_ContentType)h;
}
else{
return null;
}
}
set{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
if(value == null){
m_pHeader.RemoveAll("Content-Type");
}
else{
MIME_h h = m_pHeader.GetFirst("Content-Type");
if(h == null){
m_pHeader.Add(value);
}
else{
m_pHeader.ReplaceFirst(value);
}
}
}
}
///
/// Gets or sets base to be used for resolving relative URIs within this content part. Value null means that header field does not exist.
///
/// Is raised when this object is disposed and this property is accessed.
/// Base to be used for resolving relative URIs within this content part. See also Content-Location.
public string ContentBase
{
get{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
MIME_h h = m_pHeader.GetFirst("Content-Description");
if(h != null){
return ((MIME_h_Unstructured)h).Value;
}
else{
return null;
}
}
set{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
if(value == null){
m_pHeader.RemoveAll("Content-Base");
}
else{
MIME_h h = m_pHeader.GetFirst("Content-Base");
if(h == null){
h = new MIME_h_Unstructured("Content-Base",value);
m_pHeader.Add(h);
}
else{
((MIME_h_Unstructured)h).Value = value;
}
}
}
}
///
/// Gets or sets URI for retrieving a body part. Value null means that header field does not exist.
///
/// Is raised when this object is disposed and this property is accessed.
/// URI using which the content of this body-part part was retrieved,
/// might be retrievable, or which otherwise gives a globally unique identification of the content.
public string ContentLocation
{
get{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
MIME_h h = m_pHeader.GetFirst("Content-Description");
if(h != null){
return ((MIME_h_Unstructured)h).Value;
}
else{
return null;
}
}
set{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
if(value == null){
m_pHeader.RemoveAll("Content-Location");
}
else{
MIME_h h = m_pHeader.GetFirst("Content-Location");
if(h == null){
h = new MIME_h_Unstructured("Content-Location",value);
m_pHeader.Add(h);
}
else{
((MIME_h_Unstructured)h).Value = value;
}
}
}
}
///
/// Gets or sets content features of a MIME body part. Value null means that header field does not exist.
///
/// Is raised when this object is disposed and this property is accessed.
/// The 'Content-features:' header can be used to annotate a MIME body part with a media feature expression,
/// to indicate features of the body part content. See also RFC 2533, RFC 2506, and RFC 2045.
public string Contentfeatures
{
get{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
MIME_h h = m_pHeader.GetFirst("Content-Description");
if(h != null){
return ((MIME_h_Unstructured)h).Value;
}
else{
return null;
}
}
set{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
if(value == null){
m_pHeader.RemoveAll("Content-features");
}
else{
MIME_h h = m_pHeader.GetFirst("Content-features");
if(h == null){
h = new MIME_h_Unstructured("Content-features",value);
m_pHeader.Add(h);
}
else{
((MIME_h_Unstructured)h).Value = value;
}
}
}
}
///
/// Gets or sets content disposition. Value null means that header field does not exist.
///
/// Is raised when this object is disposed and this property is accessed.
/// Indicates whether a MIME body part is to be shown inline or is an attachment; can also indicate a
/// suggested filename for use when saving an attachment to a file.
/// Is raised when header field parsing errors.
public MIME_h_ContentDisposition ContentDisposition
{
get{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
MIME_h h = m_pHeader.GetFirst("Content-Disposition");
if(h != null){
if(!(h is MIME_h_ContentDisposition)){
throw new ParseException("Header field 'ContentDisposition' parsing failed.");
}
return (MIME_h_ContentDisposition)h;
}
else{
return null;
}
}
set{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
if(value == null){
m_pHeader.RemoveAll("Content-Disposition");
}
else{
MIME_h h = m_pHeader.GetFirst("Content-Disposition");
if(h == null){
m_pHeader.Add(value);
}
else{
m_pHeader.ReplaceFirst(value);
}
}
}
}
///
/// Gets or sets language of message content. Value null means that header field does not exist.
///
/// Is raised when this object is disposed and this property is accessed.
/// Can include a code for the natural language used in a message; e.g., 'en' for English.
/// Can also contain a list of languages for a message containing more than one language.
public string ContentLanguage
{
get{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
MIME_h h = m_pHeader.GetFirst("Content-Description");
if(h != null){
return ((MIME_h_Unstructured)h).Value;
}
else{
return null;
}
}
set{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
if(value == null){
m_pHeader.RemoveAll("Content-Language");
}
else{
MIME_h h = m_pHeader.GetFirst("Content-Language");
if(h == null){
h = new MIME_h_Unstructured("Content-Language",value);
m_pHeader.Add(h);
}
else{
((MIME_h_Unstructured)h).Value = value;
}
}
}
}
///
/// Gets or sets message alternative content. Value null means that header field does not exist.
///
/// Is raised when this object is disposed and this property is accessed.
/// Information about the media features of alternative content formats available for the current message.
public string ContentAlternative
{
get{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
MIME_h h = m_pHeader.GetFirst("Content-Description");
if(h != null){
return ((MIME_h_Unstructured)h).Value;
}
else{
return null;
}
}
set{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
if(value == null){
m_pHeader.RemoveAll("Content-Alternative");
}
else{
MIME_h h = m_pHeader.GetFirst("Content-Alternative");
if(h == null){
h = new MIME_h_Unstructured("Content-Alternative",value);
m_pHeader.Add(h);
}
else{
((MIME_h_Unstructured)h).Value = value;
}
}
}
}
///
/// Gets or sets content MD5 checksum. Value null means that header field does not exist.
///
/// Is raised when this object is disposed and this property is accessed.
/// Checksum of content to ensure that it has not been modified.
public string ContentMD5
{
get{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
MIME_h h = m_pHeader.GetFirst("Content-Description");
if(h != null){
return ((MIME_h_Unstructured)h).Value;
}
else{
return null;
}
}
set{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
if(value == null){
m_pHeader.RemoveAll("Content-MD5");
}
else{
MIME_h h = m_pHeader.GetFirst("Content-MD5");
if(h == null){
h = new MIME_h_Unstructured("Content-MD5",value);
m_pHeader.Add(h);
}
else{
((MIME_h_Unstructured)h).Value = value;
}
}
}
}
///
/// Gets or sets time duration of content. Value null means that header field does not exist.
///
/// Is raised when this object is disposed and this property is accessed.
/// Time duration of body part content, in seconds (e.g., for audio message).
public string ContentDuration
{
get{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
MIME_h h = m_pHeader.GetFirst("Content-Description");
if(h != null){
return ((MIME_h_Unstructured)h).Value;
}
else{
return null;
}
}
set{
if(m_IsDisposed){
throw new ObjectDisposedException(this.GetType().Name);
}
if(value == null){
m_pHeader.RemoveAll("Content-Duration");
}
else{
MIME_h h = m_pHeader.GetFirst("Content-Duration");
if(h == null){
h = new MIME_h_Unstructured("Content-Duration",value);
m_pHeader.Add(h);
}
else{
((MIME_h_Unstructured)h).Value = value;
}
}
}
}
///
/// Gets or sets MIME entity body.
///
/// Is raised when null reference passed.
public MIME_b Body
{
get{ return m_pBody; }
set{
if(value == null){
throw new ArgumentNullException("Body");
}
m_pBody = value;
m_pBody.SetParent(this,true);
}
}
#endregion
}
}