public void ConstructorWithContentTypeShouldSetContentType() { MimeEntity e = new MimeEntity("Hello, world", "text/silly; charset=uk-monty"); Assert.True(e.HasHeaders); Assert.True(e.HasHeader("content-type")); Assert.True(e.HasMediaType("text/silly")); Assert.Equal("text/silly; charset=uk-monty", e.ContentType); }
public void DefaultConstructorShouldCreateEmptyEntity() { MimeEntity e = new MimeEntity(); Assert.Empty(e.Headers); Assert.False(e.HasBody); Assert.False(e.HasHeaders); Assert.False(e.IsMultiPart); }
public void MimeEntityShouldHaveHeaders() { MimeEntity e = new MimeEntity("Hello, world", "text/silly"); e.ContentDisposition = "inline"; e.ContentTransferEncoding = "base64"; e.Headers.Add(new Header("foo", "bar")); Assert.True(e.HasHeader("FOO")); Assert.Equal(4, e.Headers.Count); }
/// <summary> /// Creates an entity consisting of the content and signature. /// </summary> /// <param name="algorithm">The digest algorithm used in the signature, used for the <c>micalg</c> parameter</param> /// <param name="content">The content entity that was signed.</param> /// <param name="signature">The signature entity</param> public SignedEntity(DigestAlgorithm algorithm, MimeEntity content, MimeEntity signature) : base(CreateContentType(algorithm)) { if (content == null) { throw new ArgumentNullException("content"); } Content = content; Signature = signature; }
/// <summary> /// Initializes a new instance of the supplied DSN parts. /// </summary> public DSN(DSNPerMessage perMessage, IEnumerable<DSNPerRecipient> perRecipient) : base(DSNStandard.MediaType.DSNReport) { m_explanation = new MimeEntity(); m_explanation.ContentType = MimeStandard.MediaType.TextPlain; m_notification = new MimeEntity(); m_notification.ContentType = DSNStandard.MediaType.DSNDeliveryStatus; PerRecipient = perRecipient; PerMessage = perMessage; }
internal DSN(MimeEntity explanation, HeaderCollection fields, IEnumerable<HeaderCollection> perRecipientCollection) { m_explanation = explanation; m_perMessage = new DSNPerMessage(fields); m_perRecipients = new List<DSNPerRecipient>(); foreach (HeaderCollection perRecipientFields in perRecipientCollection) { m_perRecipients.Add(new DSNPerRecipient(perRecipientFields)); } }
/// <summary>Extracts the body and associated MIME <c>Content-*</c> headers as a <see cref="MimeEntity"/></summary> /// <remarks> /// The source message has MIME and non-MIME headers, and the body is not a complete MIME entity for signing and encryption. /// Takes the source and creates new Message that contains only items relevant to Mime /// </remarks> /// <returns>The extacted MIME headers and body as a <see cref="MimeEntity"/></returns> public MimeEntity ExtractMimeEntity() { MimeEntity entity = new MimeEntity(); if (this.HasHeaders) { entity.Headers = this.Headers.SelectMimeHeaders(); if (!entity.HasHeaders) { throw new MimeException(MimeError.InvalidMimeEntity); } } if (this.HasBody) { entity.Body = new Body(this.Body); } return entity; }
/// <summary> /// Creates a signed entity from a <see cref="MimeEntity"/>, which must be multipart and have a content and signed part. /// </summary> /// <param name="source">The source entity.</param> /// <returns>The newly initialized signed entity.</returns> public static SignedEntity Load(MimeEntity source) { if (source == null) { throw new ArgumentNullException("source"); } if (!source.IsMultiPart) { throw new SignatureException(SignatureError.InvalidMultipartSigned); } return new SignedEntity(source.ParsedContentType, source.GetParts()); }
/// <summary> /// Extract DSN fields /// Fields are formatted just like MIME headers, but embedded within the Body of MimeEntity instead /// </summary> /// <param name="fieldEntity">Source entity</param> /// <returns>Collection of fields</returns> public static HeaderCollection ParseDSNFields(MimeEntity fieldEntity) { if (fieldEntity == null) { throw new ArgumentNullException("fieldEntity"); } Body DSNBody = fieldEntity.Body; if (DSNBody == null) { throw new DSNException(DSNError.InvalidDSNBody); } HeaderCollection DSNFields = null; try { DSNFields = new HeaderCollection(MimeSerializer.Default.DeserializeHeaders(DSNBody.TrimEmptyLines())); } catch (Exception ex) { throw new DSNException(DSNError.InvalidDSNFields, ex); } if (DSNFields.IsNullOrEmpty()) { throw new DSNException(DSNError.InvalidDSNFields); } return DSNFields; }
bool DecryptSignedContent(IncomingMessage message, DirectAddress recipient, out SignedCms signatures, out MimeEntity payload) { signatures = null; payload = null; foreach (X509Certificate2 cert in recipient.Certificates) { try { if (this.DecryptSignatures(message, cert, out signatures, out payload)) { // Decrypted and extracted signatures successfully return true; } } catch(Exception ex) { this.Notify(message, ex); } } return false; }
/// <summary> /// Tests the <paramref name="entity"/> to see if it is an DSN /// </summary> /// <remarks> /// DSN status is indicated by the appropriate main body <c>Content-Type</c>. The multipart body /// will contain the approprieate report-type /> /// </remarks> /// <param name="entity">The entity to test</param> /// <returns><c>true</c> if the entity is an DSN, <c>false</c> otherwise</returns> public static bool IsReport(MimeEntity entity) { if (entity == null) { return false; } ContentType contentType = entity.ParsedContentType; return (contentType.IsMediaType(MediaType.ReportMessage) && contentType.HasParameter(MediaType.ReportType, MediaType.ReportTypeValueDelivery)); }
HeaderCollection GetNotificationFields(MimeEntity notificationEntity) { return MDNParser.ParseMDNFields(notificationEntity); }
/// <summary> /// Verify Disposition-Type MDN notification /// </summary> void VerifyDSNHeaders(MimeEntity notificationEntity, DSN notification) { HeaderCollection fields = DSNParser.ParseDSNFields(notificationEntity); Assert.NotEmpty(fields); // // perMessage // Assert.True(fields.HasHeader(DSNStandard.Fields.ReportingMTA, "dns;" + ReportingMtaName)); Assert.True(fields.HasHeader(DSNStandard.Fields.OriginalMessageID, OriginalID)); // // perRecipient // Assert.True(fields.HasHeader(DSNStandard.Fields.FinalRecipient, "rfc822;" + notification.PerRecipient.First().FinalRecipient.Address)); Assert.True(fields.HasHeader(DSNStandard.Fields.Action, "failed")); Assert.True(fields.HasHeader(DSNStandard.Fields.Status, "5.0.0")); }
void Verify(MimeEntity[] mdnEntities) { Assert.True(mdnEntities.Length == 2); Assert.True(mdnEntities[1].ParsedContentType.IsMediaType(MDNStandard.MediaType.DispositionNotification)); }
public void EntityShouldHaveParsedContentType(string mediaType) { MimeEntity e = new MimeEntity("Hello, world", mediaType); Assert.Equal(mediaType, e.ParsedContentType.MediaType); }
public void TextPlainShouldNotBeMultipart() { MimeEntity e = new MimeEntity("Hello, world"); Assert.False(e.IsMultiPart); }
public void BodyTextShouldBeAccessible() { MimeEntity e = new MimeEntity("Hello, world"); Assert.Equal("Hello, world", e.Body.Text); }
public void BodySourceTextShouldBeAccessible() { MimeEntity e = new MimeEntity("Hello, world"); Assert.Equal("Hello, world", e.Body.SourceText.ToString()); }
/// <summary> /// Verify Disposition-Type MDN notification /// </summary> void VerifyDispositionTypeNotification(MimeEntity notificationEntity, Notification notification) { HeaderCollection fields = this.GetNotificationFields(notificationEntity); Assert.NotEmpty(fields); Assert.True(fields.HasHeader(MDNStandard.Fields.Disposition, notification.Disposition.ToString()), string.Format("Expected a contained Disposition-Type of {0}", notification.Disposition)); Assert.True(fields.HasHeader(MDNStandard.Fields.Gateway, "smtp;gateway.example.com")); Assert.True(fields.HasHeader(MDNStandard.Fields.OriginalMessageID, OriginalID)); if (fields[MDNStandard.Fields.Error] != null) { Assert.True(fields.HasHeader(MDNStandard.Fields.Error, ErrorMessage)); } }
/// <summary> /// Extracts the MIME entity for signing and encryption purposes. /// </summary> /// <remarks>The MIME entity for signing and encrytion consists of the <c>Content-*</c> /// MIME headers and the body as a complete MIME entity. Some clients omit the epilogue of a /// multipart message.</remarks> /// <param name="includeEpilogue">Should the epilogue be included if this the body of this message /// is multipart?</param> /// <returns>The complete MIME entity from this message for signing and encrytion.</returns> public MimeEntity ExtractEntityForSignature(bool includeEpilogue) { if (includeEpilogue || !this.IsMultiPart) { return this.ExtractMimeEntity(); } MimeEntity signableEntity = new MimeEntity(); signableEntity.Headers = this.Headers.SelectMimeHeaders(); StringSegment content = StringSegment.Null; foreach(MimePart part in this.GetAllParts()) { if (part.Type == MimePartType.BodyEpilogue) { content = new StringSegment(content.Source, content.StartIndex, part.SourceText.StartIndex - 1); } else { content.Union(part.SourceText); } } signableEntity.Body = new Body(content); return signableEntity; }
/// <summary> /// Updates this entity with a new entity, updating headers and body as appropriate. /// </summary> /// <param name="entity">The entity to update.</param> public virtual void UpdateBody(MimeEntity entity) { if (entity == null) { throw new ArgumentNullException("entity"); } this.ClearParsedHeaders(); this.Headers.AddUpdate(entity.Headers); this.Body = entity.Body; }
/// <summary> /// Retrieve MDN entites from the multiple parts of a message body /// <param name="parts">message body parts</param> /// <param name="explanation">(out) the explanation entity, if any</param> /// <param name="mdn">(out) the entity containing the MDN notification fields</param> /// </summary> public static void GetMDNEntities(IEnumerable<MimeEntity> parts, out MimeEntity explanation, out MimeEntity mdn) { if (parts == null) { throw new ArgumentNullException("parts"); } explanation = null; mdn = null; foreach (MimeEntity entity in parts) { ContentType contentType = entity.ParsedContentType; if (contentType.IsMediaType(MDNStandard.MediaType.DispositionNotification)) { if (mdn != null) { throw new MDNException(MDNError.InvalidMDNBody); } mdn = entity; } else if (contentType.IsMediaType(MimeStandard.MediaType.TextPlain)) { if (explanation != null) { throw new MDNException(MDNError.InvalidMDNBody); } explanation = entity; } } if (explanation == null || mdn == null) { throw new MDNException(MDNError.InvalidMDNBody); } }
void Verify(MimeEntity[] dsnEntities) { Assert.True(dsnEntities.Length == 2); //text/plain //future work to support text/html via a provider model. Assert.True(dsnEntities[0].ParsedContentType.IsMediaType(DSNStandard.MediaType.TextPlain)); Assert.True(dsnEntities[1].ParsedContentType.IsMediaType(DSNStandard.MediaType.DSNDeliveryStatus)); }
/// <summary> /// Extract MDN fields (RFC 3798 Section 3.1.*). /// Fields are formatted just like MIME headers, but embedded within the Body of MimeEntity instead /// </summary> /// <param name="fieldEntity">Source entity</param> /// <returns>Collection of fields</returns> public static HeaderCollection ParseMDNFields(MimeEntity fieldEntity) { if (fieldEntity == null) { throw new ArgumentNullException("fieldEntity"); } Body mdnBody = fieldEntity.Body; if (mdnBody == null) { throw new MDNException(MDNError.InvalidMDNBody); } HeaderCollection mdnFields = null; try { mdnFields = new HeaderCollection(MimeSerializer.Default.DeserializeHeaders(mdnBody.Text)); } catch(Exception ex) { throw new MDNException(MDNError.InvalidMDNFields, ex); } if (mdnFields.IsNullOrEmpty()) { throw new MDNException(MDNError.InvalidMDNFields); } return mdnFields; }
/// <summary> /// Decrypt (optionally) the given message and try to extract signatures /// </summary> bool DecryptSignatures(IncomingMessage message, X509Certificate2 certificate, out SignedCms signatures, out MimeEntity payload) { MimeEntity decryptedEntity = null; signatures = null; payload = null; if (certificate != null) { decryptedEntity = m_cryptographer.DecryptEntity(message.GetEncryptedBytes(m_cryptographer), certificate); } else { decryptedEntity = message.Message; } if (decryptedEntity == null) { return false; } if (SMIMEStandard.IsContentEnvelopedSignature(decryptedEntity.ParsedContentType)) { signatures = m_cryptographer.DeserializeEnvelopedSignature(decryptedEntity); payload = MimeSerializer.Default.Deserialize<MimeEntity>(signatures.ContentInfo.Content); } else if (SMIMEStandard.IsContentMultipartSignature(decryptedEntity.ParsedContentType)) { SignedEntity signedEntity = SignedEntity.Load(decryptedEntity); signatures = m_cryptographer.DeserializeDetachedSignature(signedEntity); payload = signedEntity.Content; } else { throw new AgentException(AgentError.UnsignedMessage); } return true; }
/// <summary> /// Tests the <paramref name="entity"/> to see if it contains an MDN request. /// </summary> /// <param name="entity">The entity to test</param> /// <returns><c>true</c> if the entity contains an MDN request, <c>false</c> otherwise</returns> public static bool HasMDNRequest(MimeEntity entity) { if (entity == null) { return false; } return entity.HasHeader(Headers.DispositionNotificationTo); }
/// <summary> /// Retrieve DSN entites from the multiple parts of a message body /// <param name="parts">message body parts</param> /// <param name="explanation">(out) the explanation entity, if any</param> /// <param name="deliveryStatus">(out) the entity containing the DSN fields</param> /// </summary> public static void GetDSNEntities(IEnumerable<MimeEntity> parts, out MimeEntity explanation, out MimeEntity deliveryStatus) { if (parts == null) { throw new ArgumentNullException("parts"); } explanation = null; deliveryStatus = null; foreach (MimeEntity entity in parts) { ContentType contentType = entity.ParsedContentType; if (contentType.IsMediaType(DSNStandard.MediaType.DSNDeliveryStatus)) { if (deliveryStatus != null) { throw new DSNException(DSNError.InvalidDSNBody); } deliveryStatus = entity; } else if (contentType.IsMediaType(MimeStandard.MediaType.TextPlain)) { if (explanation != null) { throw new DSNException(DSNError.InvalidDSNBody); } explanation = entity; } } if (explanation == null || deliveryStatus == null) { throw new DSNException(DSNError.InvalidDSNBody); } }
/// <summary> /// Tests the <paramref name="entity"/> to see if it is an MDN /// </summary> /// <remarks> /// MDN status is indicated by the appropriate main body <c>Content-Type</c>. The multipart body /// will contain the actual disposition notification (see <see cref="MDNStandard.IsNotification"/> /// </remarks> /// <param name="entity">The entity to test</param> /// <returns><c>true</c> if the entity is an MDN, <c>false</c> otherwise</returns> public static bool IsReport(MimeEntity entity) { if (entity == null) { return false; } ContentType contentType = entity.ParsedContentType; return (contentType.IsMediaType(MDNStandard.MediaType.ReportMessage) && contentType.HasParameter(MDNStandard.ReportType, MDNStandard.ReportTypeValueNotification)); }
/// <summary> /// Extract per-recipient dsn headers as a collection /// Fields are formatted just like MIME headers, but embedded within the Body of MimeEntity instead /// </summary> /// <param name="fieldEntity">Source entity</param> /// <returns>Collection of <see cref="HeaderCollection"/></returns> public static List<HeaderCollection> ParsePerRecientFields(MimeEntity fieldEntity) { if (fieldEntity == null) { throw new ArgumentNullException("fieldEntity"); } Body dsnBody = fieldEntity.Body; if (dsnBody == null) { throw new DSNException(DSNError.InvalidDSNBody); } HeaderCollection dsnFields = new HeaderCollection(); try { dsnFields.Add(new HeaderCollection(MimeSerializer.Default.DeserializeHeaders(dsnBody.PerRecipientSeperator())) , PerRecipientFieldList()); } catch (Exception ex) { throw new DSNException(DSNError.InvalidDSNFields, ex); } if (dsnFields.IsNullOrEmpty()) { throw new DSNException(DSNError.InvalidDSNFields); } return PerRecipientList(dsnFields); }
/// <summary> /// Tests the entity to determine if it is a disposition notification body part /// </summary> /// <remarks> /// Notification status is indicated by the appropriate <c>Content-Type</c>. The notification /// section will be a body part of the appropriate MDN report multipart body. /// </remarks> /// <param name="entity">The entity to test</param> /// <returns><c>true</c> if this body part is an MDN notification, <c>false</c> otherwise</returns> public static bool IsNotification(MimeEntity entity) { if (entity == null) { return false; } return entity.HasMediaType(MDNStandard.MediaType.DispositionNotification); }