public void TestMessageIdHeaderFolding () { var header = new Header ("Message-Id", string.Format ("<{0}@princeton-plainsboro-hospital.com>", Guid.NewGuid ())); var expected = " " + header.Value + FormatOptions.Default.NewLine; var raw = ByteArrayToString (header.RawValue); Assert.IsTrue (raw[raw.Length - 1] == '\n', "The RawValue does not end with a new line."); Assert.AreEqual (expected, raw, "The folded Message-Id header does not match the expected value."); }
public void TestCloning () { var header = new Header (HeaderId.Comments, "These are some comments."); var clone = header.Clone (); Assert.AreEqual (header.Id, clone.Id, "The cloned header id does not match."); Assert.AreEqual (header.Field, clone.Field, "The cloned header field does not match."); Assert.AreEqual (header.Value, clone.Value, "The cloned header value does not match."); Assert.AreEqual (header.RawField, clone.RawField, "The cloned header raw field does not match."); Assert.AreEqual (header.RawValue, clone.RawValue, "The cloned header raw value does not match."); }
public void TestHeaderFolding() { var header = new Header ("Subject", "This is a subject value that should be long enough to force line wrapping to keep the line length under the 72 character limit."); var raw = ByteArrayToString (header.RawValue); Assert.IsTrue (raw[raw.Length - 1] == '\n', "The RawValue does not end with a new line."); Assert.IsTrue (GetMaxLineLength (raw) < FormatOptions.Default.MaxLineLength, "The RawValue is not folded properly."); var unfolded = Header.Unfold (raw); Assert.AreEqual (header.Value, unfolded, "Unfolded header does not match the original header value."); }
public void TestAddressHeaderFolding () { var expected = " Jeffrey Stedfast <*****@*****.**>, \"Jeffrey A. Stedfast\"" + FormatOptions.Default.NewLine + "\t<*****@*****.**>, \"Dr. Gregory House, M.D.\"" + FormatOptions.Default.NewLine + "\t<*****@*****.**>" + FormatOptions.Default.NewLine; var header = new Header ("To", "Jeffrey Stedfast <*****@*****.**>, \"Jeffrey A. Stedfast\" <*****@*****.**>, \"Dr. Gregory House, M.D.\" <*****@*****.**>"); var raw = ByteArrayToString (header.RawValue); Assert.IsTrue (raw[raw.Length - 1] == '\n', "The RawValue does not end with a new line."); Assert.IsTrue (GetMaxLineLength (raw) < FormatOptions.Default.MaxLineLength, "The RawValue is not folded properly."); Assert.AreEqual (expected, raw, "The folded address header does not match the expected value."); }
public void TestReceivedHeaderFolding() { var header = new Header ("Received", ""); foreach (var received in ReceivedHeaderValues) { header.SetValue (Encoding.ASCII, received.Replace (FormatOptions.Default.NewLine + "\t", " ").Trim ()); var raw = ByteArrayToString (header.RawValue); Assert.IsTrue (raw[raw.Length - 1] == '\n', "The RawValue does not end with a new line."); Assert.AreEqual (received + FormatOptions.Default.NewLine, raw, "The folded Received header does not match the expected value."); } }
public void TestArgumentExceptions () { var header = new Header ("utf-8", HeaderId.Subject, "This is a subject..."); Assert.Throws<ArgumentOutOfRangeException> (() => new Header (HeaderId.Unknown, "value")); Assert.Throws<ArgumentNullException> (() => new Header (HeaderId.Subject, null)); Assert.Throws<ArgumentNullException> (() => new Header (null, "value")); Assert.Throws<ArgumentException> (() => new Header (string.Empty, "value")); Assert.Throws<ArgumentNullException> (() => new Header ("field", null)); Assert.Throws<ArgumentNullException> (() => new Header ((Encoding) null, HeaderId.Subject, "value")); Assert.Throws<ArgumentOutOfRangeException> (() => new Header (Encoding.UTF8, HeaderId.Unknown, "value")); Assert.Throws<ArgumentNullException> (() => new Header (Encoding.UTF8, HeaderId.Subject, null)); Assert.Throws<ArgumentNullException> (() => new Header ((string) null, "field", "value")); Assert.Throws<ArgumentNullException> (() => new Header ("utf-8", null, "value")); Assert.Throws<ArgumentException> (() => new Header ("utf-8", string.Empty, "value")); Assert.Throws<ArgumentNullException> (() => new Header ("utf-8", "field", null)); Assert.Throws<ArgumentNullException> (() => new Header ((Encoding) null, "field", "value")); Assert.Throws<ArgumentNullException> (() => new Header (Encoding.UTF8, null, "value")); Assert.Throws<ArgumentException> (() => new Header (Encoding.UTF8, string.Empty, "value")); Assert.Throws<ArgumentNullException> (() => new Header (Encoding.UTF8, "field", null)); Assert.Throws<ArgumentNullException> (() => new Header ((string) null, HeaderId.Subject, "value")); Assert.Throws<ArgumentOutOfRangeException> (() => new Header ("utf-8", HeaderId.Unknown, "value")); Assert.Throws<ArgumentNullException> (() => new Header ("utf-8", HeaderId.Subject, null)); // GetValue Assert.Throws<ArgumentNullException> (() => header.GetValue ((Encoding) null)); Assert.Throws<ArgumentNullException> (() => header.GetValue ((string) null)); // SetValue Assert.Throws<ArgumentNullException> (() => header.SetValue (null, Encoding.UTF8, "value")); Assert.Throws<ArgumentNullException> (() => header.SetValue (FormatOptions.Default, (Encoding) null, "value")); Assert.Throws<ArgumentNullException> (() => header.SetValue (FormatOptions.Default, Encoding.UTF8, null)); Assert.Throws<ArgumentNullException> (() => header.SetValue (null, "utf-8", "value")); Assert.Throws<ArgumentNullException> (() => header.SetValue (FormatOptions.Default, (string) null, "value")); Assert.Throws<ArgumentNullException> (() => header.SetValue (FormatOptions.Default, "utf-8", null)); Assert.Throws<ArgumentNullException> (() => header.SetValue ((Encoding) null, "value")); Assert.Throws<ArgumentNullException> (() => header.SetValue (Encoding.UTF8, null)); Assert.Throws<ArgumentNullException> (() => header.SetValue ((string) null, "value")); Assert.Throws<ArgumentNullException> (() => header.SetValue ("utf-8", null)); }
/// <summary> /// Tries to parse the given text into a new <see cref="MimeKit.Header"/> instance. /// </summary> /// <remarks> /// Parses a header from the specified text. /// </remarks> /// <returns><c>true</c>, if the header was successfully parsed, <c>false</c> otherwise.</returns> /// <param name="text">The text to parse.</param> /// <param name="header">The parsed header.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="text"/> is <c>null</c>. /// </exception> public static bool TryParse (string text, out Header header) { return TryParse (ParserOptions.Default, text, out header); }
/// <summary> /// Tries to parse the given text into a new <see cref="MimeKit.Header"/> instance. /// </summary> /// <remarks> /// Parses a header from the specified text. /// </remarks> /// <returns><c>true</c>, if the header was successfully parsed, <c>false</c> otherwise.</returns> /// <param name="options">The parser options to use.</param> /// <param name="text">The text to parse.</param> /// <param name="header">The parsed header.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="options"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="text"/> is <c>null</c>.</para> /// </exception> public static bool TryParse (ParserOptions options, string text, out Header header) { if (options == null) throw new ArgumentNullException ("options"); if (text == null) throw new ArgumentNullException ("text"); var buffer = Encoding.UTF8.GetBytes (text); unsafe { fixed (byte *inptr = buffer) { return TryParse (options.Clone (), inptr, buffer.Length, true, out header); } } }
/// <summary> /// Called when the headers change in some way. /// </summary> /// <remarks> /// <para>Whenever a header is added, changed, or removed, this method will /// be called in order to allow custom <see cref="MimeEntity"/> subclasses /// to update their state.</para> /// <para>Overrides of this method should call the base method so that their /// superclass may also update its own state.</para> /// </remarks> /// <param name="action">The type of change.</param> /// <param name="header">The header being added, changed or removed.</param> protected virtual void OnHeadersChanged (HeaderListChangedAction action, Header header) { string text; switch (action) { case HeaderListChangedAction.Added: case HeaderListChangedAction.Changed: switch (header.Id) { case HeaderId.ContentDisposition: if (disposition != null) disposition.Changed -= ContentDispositionChanged; if (ContentDisposition.TryParse (Headers.Options, header.RawValue, out disposition)) disposition.Changed += ContentDispositionChanged; break; case HeaderId.ContentLocation: text = header.Value.Trim (); if (Uri.IsWellFormedUriString (text, UriKind.Absolute)) location = new Uri (text, UriKind.Absolute); else if (Uri.IsWellFormedUriString (text, UriKind.Relative)) location = new Uri (text, UriKind.Relative); else location = null; break; case HeaderId.ContentBase: text = header.Value.Trim (); if (Uri.IsWellFormedUriString (text, UriKind.Absolute)) baseUri = new Uri (text, UriKind.Absolute); else baseUri = null; break; case HeaderId.ContentId: contentId = MimeUtils.EnumerateReferences (header.RawValue, 0, header.RawValue.Length).FirstOrDefault (); break; } break; case HeaderListChangedAction.Removed: switch (header.Id) { case HeaderId.ContentDisposition: if (disposition != null) disposition.Changed -= ContentDispositionChanged; disposition = null; break; case HeaderId.ContentLocation: location = null; break; case HeaderId.ContentBase: baseUri = null; break; case HeaderId.ContentId: contentId = null; break; } break; case HeaderListChangedAction.Cleared: if (disposition != null) disposition.Changed -= ContentDispositionChanged; disposition = null; contentId = null; location = null; baseUri = null; break; default: throw new ArgumentOutOfRangeException ("action"); } }
void AddAddresses (Header header, InternetAddressList list) { int length = header.RawValue.Length; List<InternetAddress> parsed; int index = 0; // parse the addresses in the new header and add them to our address list if (!InternetAddressList.TryParse (Headers.Options, header.RawValue, ref index, length, false, false, out parsed)) return; list.Changed -= InternetAddressListChanged; list.AddRange (parsed); list.Changed += InternetAddressListChanged; }
/// <summary> /// Verify the specified DKIM-Signature header. /// </summary> /// <remarks> /// Verifies the specified DKIM-Signature header. /// </remarks> /// <param name="options">The formatting options.</param> /// <param name="dkimSignature">The DKIM-Signature header.</param> /// <param name="publicKeyLocator">The public key locator service.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="options"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="dkimSignature"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="publicKeyLocator"/> is <c>null</c>.</para> /// </exception> /// <exception cref="System.ArgumentException"> /// <paramref name="dkimSignature"/> is not a DKIM-Signature header. /// </exception> /// <exception cref="System.FormatException"> /// The DKIM-Signature header value is malformed. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> bool Verify (FormatOptions options, Header dkimSignature, IDkimPublicKeyLocator publicKeyLocator, CancellationToken cancellationToken = default (CancellationToken)) { if (options == null) throw new ArgumentNullException ("options"); if (dkimSignature == null) throw new ArgumentNullException ("dkimSignature"); if (dkimSignature.Id != HeaderId.DkimSignature) throw new ArgumentException ("The dkimSignature parameter MUST be a DKIM-Signature header.", "dkimSignature"); if (publicKeyLocator == null) throw new ArgumentNullException ("publicKeyLocator"); var parameters = ParseDkimSignature (dkimSignature.Value); DkimCanonicalizationAlgorithm headerAlgorithm, bodyAlgorithm; DkimSignatureAlgorithm signatureAlgorithm; AsymmetricKeyParameter key; string d, s, q, h, bh, b; int maxLength; ValidateDkimSignatureParameters (parameters, out signatureAlgorithm, out headerAlgorithm, out bodyAlgorithm, out d, out s, out q, out h, out bh, out b, out maxLength); key = publicKeyLocator.LocatePublicKey (q, d, s, cancellationToken); options = options.Clone (); options.NewLineFormat = NewLineFormat.Dos; // first check the body hash (if that's invalid, then the entire signature is invalid) var hash = Convert.ToBase64String (DkimHashBody (options, signatureAlgorithm, bodyAlgorithm, maxLength)); if (hash != bh) return false; using (var stream = new DkimSignatureStream (DkimGetDigestSigner (signatureAlgorithm, key))) { using (var filtered = new FilteredStream (stream)) { filtered.Add (options.CreateNewLineFilter ()); DkimWriteHeaders (options, h.Split (':'), headerAlgorithm, filtered); // now include the DKIM-Signature header that we are verifying, // but only after removing the "b=" signature value. var header = GetSignedDkimSignatureHeader (dkimSignature); switch (headerAlgorithm) { case DkimCanonicalizationAlgorithm.Relaxed: DkimWriteHeaderRelaxed (options, filtered, header); break; default: DkimWriteHeaderSimple (options, filtered, header); break; } filtered.Flush (); } return stream.VerifySignature (b); } }
/// <summary> /// Digitally sign the message using a DomainKeys Identified Mail (DKIM) signature. /// </summary> /// <remarks> /// Digitally signs the message using a DomainKeys Identified Mail (DKIM) signature. /// </remarks> /// <param name="options">The formatting options.</param> /// <param name="signer">The DKIM signer.</param> /// <param name="headers">The list of header fields to sign.</param> /// <param name="headerCanonicalizationAlgorithm">The header canonicalization algorithm.</param> /// <param name="bodyCanonicalizationAlgorithm">The body canonicalization algorithm.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="options"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="signer"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="headers"/> is <c>null</c>.</para> /// </exception> /// <exception cref="System.ArgumentException"> /// <para><paramref name="headers"/> does not contain the 'From' header.</para> /// <para>-or-</para> /// <para><paramref name="headers"/> contains one or more of the following headers: Return-Path, /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature.</para> /// </exception> void Sign (FormatOptions options, DkimSigner signer, IList<HeaderId> headers, DkimCanonicalizationAlgorithm headerCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple) { if (options == null) throw new ArgumentNullException ("options"); if (signer == null) throw new ArgumentNullException ("signer"); if (headers == null) throw new ArgumentNullException ("headers"); if (!headers.Contains (HeaderId.From)) throw new ArgumentException ("The list of headers to sign MUST include the 'From' header."); var fields = new string[headers.Count]; for (int i = 0; i < headers.Count; i++) { if (DkimShouldNotInclude.Contains (headers[i])) throw new ArgumentException (string.Format ("The list of headers to sign SHOULD NOT include the '{0}' header.", headers[i].ToHeaderName ())); fields[i] = headers[i].ToHeaderName ().ToLowerInvariant (); } if (version == null && Body != null && Body.Headers.Count > 0) MimeVersion = new Version (1, 0); Prepare (EncodingConstraint.SevenBit, 78); var t = DateTime.Now - DateUtils.UnixEpoch; var value = new StringBuilder ("v=1"); byte[] signature, hash; Header dkim; options = options.Clone (); options.NewLineFormat = NewLineFormat.Dos; switch (signer.SignatureAlgorithm) { case DkimSignatureAlgorithm.RsaSha256: value.Append ("; a=rsa-sha256"); break; default: value.Append ("; a=rsa-sha1"); break; } value.AppendFormat ("; d={0}; s={1}", signer.Domain, signer.Selector); value.AppendFormat ("; c={0}/{1}", headerCanonicalizationAlgorithm.ToString ().ToLowerInvariant (), bodyCanonicalizationAlgorithm.ToString ().ToLowerInvariant ()); if (!string.IsNullOrEmpty (signer.QueryMethod)) value.AppendFormat ("; q={0}", signer.QueryMethod); if (!string.IsNullOrEmpty (signer.AgentOrUserIdentifier)) value.AppendFormat ("; i={0}", signer.AgentOrUserIdentifier); value.AppendFormat ("; t={0}", (long) t.TotalSeconds); using (var stream = new DkimSignatureStream (DkimGetDigestSigner (signer.SignatureAlgorithm, signer.PrivateKey))) { using (var filtered = new FilteredStream (stream)) { filtered.Add (options.CreateNewLineFilter ()); // write the specified message headers DkimWriteHeaders (options, fields, headerCanonicalizationAlgorithm, filtered); value.AppendFormat ("; h={0}", string.Join (":", fields.ToArray ())); hash = DkimHashBody (options, signer.SignatureAlgorithm, bodyCanonicalizationAlgorithm, -1); value.AppendFormat ("; bh={0}", Convert.ToBase64String (hash)); value.Append ("; b="); dkim = new Header (HeaderId.DkimSignature, value.ToString ()); Headers.Insert (0, dkim); switch (headerCanonicalizationAlgorithm) { case DkimCanonicalizationAlgorithm.Relaxed: DkimWriteHeaderRelaxed (options, filtered, dkim); break; default: DkimWriteHeaderSimple (options, filtered, dkim); break; } filtered.Flush (); } signature = stream.GenerateSignature (); dkim.Value += Convert.ToBase64String (signature); } }
static void DkimWriteHeaderRelaxed (FormatOptions options, Stream stream, Header header) { var name = Encoding.ASCII.GetBytes (header.Field.ToLowerInvariant ()); var rawValue = header.GetRawValue (options); int index = 0; stream.Write (name, 0, name.Length); stream.WriteByte ((byte) ':'); // look for the first non-whitespace character while (index < rawValue.Length && rawValue[index].IsBlank ()) index++; while (index < rawValue.Length) { int startIndex = index; int endIndex, nextLine; // look for the first non-whitespace character while (index < rawValue.Length && rawValue[index].IsBlank ()) index++; // look for the end of the line endIndex = index; while (endIndex < rawValue.Length && rawValue[endIndex] != (byte) '\n') endIndex++; nextLine = endIndex + 1; if (endIndex > index && rawValue[endIndex - 1] == (byte) '\r') endIndex--; if (index > startIndex) stream.WriteByte ((byte) ' '); while (index < endIndex) { startIndex = index; while (index < endIndex && !rawValue[index].IsBlank ()) index++; stream.Write (rawValue, startIndex, index - startIndex); startIndex = index; while (index < endIndex && rawValue[index].IsBlank ()) index++; if (index > startIndex) stream.WriteByte ((byte) ' '); } index = nextLine; } stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); }
static void DkimWriteHeaderSimple (FormatOptions options, Stream stream, Header header, bool isDkimSignature) { var rawValue = header.GetRawValue (options); int rawLength = rawValue.Length; if (isDkimSignature && rawLength > 0) { if (rawValue[rawLength - 1] == (byte) '\n') { rawLength--; if (rawLength > 0 && rawValue[rawLength - 1] == (byte) '\r') rawLength--; } } stream.Write (header.RawField, 0, header.RawField.Length); stream.Write (new [] { (byte) ':' }, 0, 1); stream.Write (rawValue, 0, rawLength); }
/// <summary> /// Called when the headers change in some way. /// </summary> /// <param name="action">The type of change.</param> /// <param name="header">The header being added, changed or removed.</param> protected virtual void OnHeadersChanged(HeaderListChangedAction action, Header header) { switch (action) { case HeaderListChangedAction.Added: case HeaderListChangedAction.Changed: switch (header.Id) { case HeaderId.ContentDisposition: if (disposition != null) disposition.Changed -= ContentDispositionChanged; if (ContentDisposition.TryParse (Headers.Options, header.RawValue, out disposition)) disposition.Changed += ContentDispositionChanged; break; case HeaderId.ContentId: contentId = MimeUtils.EnumerateReferences (header.RawValue, 0, header.RawValue.Length).FirstOrDefault (); break; } break; case HeaderListChangedAction.Removed: switch (header.Id) { case HeaderId.ContentDisposition: if (disposition != null) disposition.Changed -= ContentDispositionChanged; disposition = null; break; case HeaderId.ContentId: contentId = null; break; } break; case HeaderListChangedAction.Cleared: if (disposition != null) disposition.Changed -= ContentDispositionChanged; disposition = null; contentId = null; break; default: throw new ArgumentOutOfRangeException (); } }
public void TestArgumentExceptions () { var list = new HeaderList (); Header header; string value; // Add Assert.Throws<ArgumentNullException> (() => list.Add (null)); Assert.Throws<ArgumentOutOfRangeException> (() => list.Add (HeaderId.Unknown, "value")); Assert.Throws<ArgumentNullException> (() => list.Add (HeaderId.AdHoc, null)); Assert.Throws<ArgumentNullException> (() => list.Add (null, "value")); Assert.Throws<ArgumentNullException> (() => list.Add ("field", null)); Assert.Throws<ArgumentOutOfRangeException> (() => list.Add (HeaderId.Unknown, Encoding.UTF8, "value")); Assert.Throws<ArgumentNullException> (() => list.Add (HeaderId.AdHoc, null, "value")); Assert.Throws<ArgumentNullException> (() => list.Add (HeaderId.AdHoc, Encoding.UTF8, null)); Assert.Throws<ArgumentNullException> (() => list.Add (null, Encoding.UTF8, "value")); Assert.Throws<ArgumentNullException> (() => list.Add ("field", null, "value")); Assert.Throws<ArgumentNullException> (() => list.Add ("field", Encoding.UTF8, null)); // Contains Assert.Throws<ArgumentOutOfRangeException> (() => list.Contains (HeaderId.Unknown)); Assert.Throws<ArgumentNullException> (() => list.Contains ((Header) null)); Assert.Throws<ArgumentNullException> (() => list.Contains ((string) null)); // CopyTo Assert.Throws<ArgumentOutOfRangeException> (() => list.CopyTo (new Header[0], -1)); Assert.Throws<ArgumentNullException> (() => list.CopyTo (null, 0)); // IndexOf Assert.Throws<ArgumentOutOfRangeException> (() => list.IndexOf (HeaderId.Unknown)); Assert.Throws<ArgumentNullException> (() => list.IndexOf ((Header) null)); Assert.Throws<ArgumentNullException> (() => list.IndexOf ((string) null)); // Insert list.Add ("field", "value"); Assert.Throws<ArgumentOutOfRangeException> (() => list.Insert (-1, new Header (HeaderId.AdHoc, "value"))); Assert.Throws<ArgumentOutOfRangeException> (() => list.Insert (-1, HeaderId.AdHoc, Encoding.UTF8, "value")); Assert.Throws<ArgumentOutOfRangeException> (() => list.Insert (-1, "field", Encoding.UTF8, "value")); Assert.Throws<ArgumentOutOfRangeException> (() => list.Insert (-1, HeaderId.AdHoc, "value")); Assert.Throws<ArgumentOutOfRangeException> (() => list.Insert (-1, "field", "value")); Assert.Throws<ArgumentOutOfRangeException> (() => list.Insert (0, HeaderId.Unknown, Encoding.UTF8, "value")); Assert.Throws<ArgumentOutOfRangeException> (() => list.Insert (0, HeaderId.Unknown, "value")); Assert.Throws<ArgumentNullException> (() => list.Insert (0, HeaderId.AdHoc, Encoding.UTF8, null)); Assert.Throws<ArgumentNullException> (() => list.Insert (0, HeaderId.AdHoc, null, "value")); Assert.Throws<ArgumentNullException> (() => list.Insert (0, HeaderId.AdHoc, null)); Assert.Throws<ArgumentNullException> (() => list.Insert (0, null, "value")); Assert.Throws<ArgumentNullException> (() => list.Insert (0, "field", null)); Assert.Throws<ArgumentNullException> (() => list.Insert (0, null)); // LastIndexOf Assert.Throws<ArgumentOutOfRangeException> (() => list.LastIndexOf (HeaderId.Unknown)); Assert.Throws<ArgumentNullException> (() => list.LastIndexOf ((string) null)); // Remove Assert.Throws<ArgumentOutOfRangeException> (() => list.Remove (HeaderId.Unknown)); Assert.Throws<ArgumentNullException> (() => list.Remove ((Header) null)); Assert.Throws<ArgumentNullException> (() => list.Remove ((string) null)); // RemoveAll Assert.Throws<ArgumentOutOfRangeException> (() => list.RemoveAll (HeaderId.Unknown)); Assert.Throws<ArgumentNullException> (() => list.RemoveAll ((string) null)); // RemoveAt Assert.Throws<ArgumentOutOfRangeException> (() => list.RemoveAt (-1)); // Replace Assert.Throws<ArgumentNullException> (() => list.Replace (null)); Assert.Throws<ArgumentOutOfRangeException> (() => list.Replace (HeaderId.Unknown, "value")); Assert.Throws<ArgumentNullException> (() => list.Replace (HeaderId.AdHoc, null)); Assert.Throws<ArgumentNullException> (() => list.Replace (null, "value")); Assert.Throws<ArgumentNullException> (() => list.Replace ("field", null)); Assert.Throws<ArgumentOutOfRangeException> (() => list.Replace (HeaderId.Unknown, Encoding.UTF8, "value")); Assert.Throws<ArgumentNullException> (() => list.Replace (HeaderId.AdHoc, null, "value")); Assert.Throws<ArgumentNullException> (() => list.Replace (HeaderId.AdHoc, Encoding.UTF8, null)); Assert.Throws<ArgumentNullException> (() => list.Replace (null, Encoding.UTF8, "value")); Assert.Throws<ArgumentNullException> (() => list.Replace ("field", null, "value")); Assert.Throws<ArgumentNullException> (() => list.Replace ("field", Encoding.UTF8, null)); using (var stream = new MemoryStream ()) { // Load Assert.Throws<ArgumentNullException> (() => HeaderList.Load (ParserOptions.Default, (Stream) null)); Assert.Throws<ArgumentNullException> (() => HeaderList.Load (ParserOptions.Default, (string) null)); Assert.Throws<ArgumentNullException> (() => HeaderList.Load (null, stream)); Assert.Throws<ArgumentNullException> (() => HeaderList.Load ((Stream) null)); Assert.Throws<ArgumentNullException> (() => HeaderList.Load ((string) null)); // WriteTo Assert.Throws<ArgumentNullException> (() => list.WriteTo (FormatOptions.Default, null)); Assert.Throws<ArgumentNullException> (() => list.WriteTo (null, stream)); Assert.Throws<ArgumentNullException> (() => list.WriteTo (null)); } // Indexers Assert.Throws<ArgumentOutOfRangeException> (() => list[-1] = new Header (HeaderId.AdHoc, "value")); Assert.Throws<ArgumentOutOfRangeException> (() => list[HeaderId.Unknown] = "value"); Assert.Throws<ArgumentOutOfRangeException> (() => value = list[HeaderId.Unknown]); Assert.Throws<ArgumentOutOfRangeException> (() => header = list[-1]); Assert.Throws<ArgumentNullException> (() => list[HeaderId.AdHoc] = null); Assert.Throws<ArgumentNullException> (() => value = list[null]); Assert.Throws<ArgumentNullException> (() => list[null] = "value"); Assert.Throws<ArgumentNullException> (() => list["field"] = null); Assert.Throws<ArgumentNullException> (() => list[0] = null); }
public void TestDkimSignatureHeaderFolding () { var header = new Header ("UTF-8", "DKIM-Signature", "v=1; a=rsa-sha256; c=simple/simple; d=maillist.codeproject.com; s=mail; t=1435835767; bh=tiafHSAvEg4GPJlbkR6e7qr1oydTj+ZXs392TcHwwvs=; h=MIME-Version:From:To:Date:Subject:Content-Type:Content-Transfer-Encoding:Message-Id; b=Qtgo0bWwT0H18CxD2+ey8/382791TBNYtZ8VOLlXxxsbw5fab8uEo53o5tPun6kNx4khmJx/yWowvrCOAcMoqgNO7Hb7JB8NR7eNyOvtLKCG34AfDZyHNcTZHR/QnBpRKHssu5w2CQDUAjKnuGKRW95LCMMX3r924dErZOJnGhs="); var expected = " v=1; a=rsa-sha256; c=simple/simple;\n\td=maillist.codeproject.com; s=mail; t=1435835767;\n\tbh=tiafHSAvEg4GPJlbkR6e7qr1oydTj+ZXs392TcHwwvs=;\n\th=MIME-Version:From:To:Date:Subject:Content-Type:Content-Transfer-Encoding:\n\tMessage-Id;\n\tb=Qtgo0bWwT0H18CxD2+ey8/382791TBNYtZ8VOLlXxxsbw5fab8uEo53o5tPun6kNx4khmJx/yWo\n\twvrCOAcMoqgNO7Hb7JB8NR7eNyOvtLKCG34AfDZyHNcTZHR/QnBpRKHssu5w2CQDUAjKnuGKRW95L\n\tCMMX3r924dErZOJnGhs=\n"; var raw = ByteArrayToString (header.RawValue); expected = expected.Replace ("\n", Environment.NewLine); Assert.AreEqual (expected, raw, "The RawValue does not match the expected value."); }
public void TestReferencesHeaderFolding () { var expected = new StringBuilder (); expected.AppendFormat (" <{0}@princeton-plainsboro-hospital.com>", Guid.NewGuid ()); for (int i = 0; i < 5; i++) expected.AppendFormat ("{0}\t<{1}@princeton-plainsboro-hospital.com>", FormatOptions.Default.NewLine, Guid.NewGuid ()); expected.Append (FormatOptions.Default.NewLine); var header = new Header ("References", expected.ToString ()); var raw = ByteArrayToString (header.RawValue); Assert.IsTrue (raw[raw.Length - 1] == '\n', "The RawValue does not end with a new line."); Assert.AreEqual (expected.ToString (), raw, "The folded References header does not match the expected value."); }
/// <summary> /// Called when the headers change in some way. /// </summary> /// <remarks> /// Updates the <see cref="ContentTransferEncoding"/>, <see cref="ContentDuration"/>, /// and <see cref="ContentMd5"/> properties if the corresponding headers have changed. /// </remarks> /// <param name="action">The type of change.</param> /// <param name="header">The header being added, changed or removed.</param> protected override void OnHeadersChanged (HeaderListChangedAction action, Header header) { int value; base.OnHeadersChanged (action, header); switch (action) { case HeaderListChangedAction.Added: case HeaderListChangedAction.Changed: switch (header.Id) { case HeaderId.ContentTransferEncoding: MimeUtils.TryParse (header.Value, out encoding); break; case HeaderId.ContentDuration: if (int.TryParse (header.Value, out value)) duration = value; else duration = null; break; case HeaderId.ContentMd5: md5sum = header.Value.Trim (); break; } break; case HeaderListChangedAction.Removed: switch (header.Id) { case HeaderId.ContentTransferEncoding: encoding = ContentEncoding.Default; break; case HeaderId.ContentDuration: duration = null; break; case HeaderId.ContentMd5: md5sum = null; break; } break; case HeaderListChangedAction.Cleared: encoding = ContentEncoding.Default; duration = null; md5sum = null; break; default: throw new ArgumentOutOfRangeException ("action"); } }
/// <summary> /// Clone the header. /// </summary> /// <remarks> /// Clones the header, copying the current RawValue. /// </remarks> /// <returns>A copy of the header with its current state.</returns> public Header Clone () { var header = new Header (Options, Id, Field, RawField, RawValue); // if the textValue has already been calculated, set it on the cloned header as well. header.textValue = textValue; return header; }
public void TestArgumentExceptions () { var locator = new DummyPublicKeyLocator (DkimKeys.Public); var dkimHeader = new Header (HeaderId.DkimSignature, "value"); var message = new MimeMessage (); Assert.Throws<ArgumentNullException> (() => message.Sign (null, new HeaderId[] { HeaderId.From })); Assert.Throws<ArgumentNullException> (() => message.Sign (CreateSigner (DkimSignatureAlgorithm.RsaSha1), null)); Assert.Throws<ArgumentNullException> (() => message.Verify (null, locator)); Assert.Throws<ArgumentNullException> (() => message.Verify (dkimHeader, null)); Assert.Throws<ArgumentNullException> (() => message.Verify (null, dkimHeader, locator)); Assert.Throws<ArgumentNullException> (() => message.Verify (FormatOptions.Default, null, locator)); Assert.Throws<ArgumentNullException> (() => message.Verify (FormatOptions.Default, dkimHeader, null)); }
static Header GetSignedDkimSignatureHeader (Header dkimSignature) { // modify the raw DKIM-Signature header value by chopping off the signature value after the "b=" var rawValue = (byte[]) dkimSignature.RawValue.Clone (); int length = 0, index = 0; do { while (index < rawValue.Length && rawValue[index].IsWhitespace ()) index++; if (index + 2 < rawValue.Length) { var param = (char) rawValue[index++]; while (index < rawValue.Length && rawValue[index].IsWhitespace ()) index++; if (index < rawValue.Length && rawValue[index] == (byte) '=' && param == 'b') { length = ++index; while (index < rawValue.Length && rawValue[index] != (byte) ';') index++; if (index == rawValue.Length && rawValue[index - 1] == (byte) '\n') { index--; if (rawValue[index - 1] == (byte) '\r') index--; } break; } } while (index < rawValue.Length && rawValue[index] != (byte) ';') index++; if (index < rawValue.Length) index++; } while (index < rawValue.Length); if (index == rawValue.Length) throw new FormatException ("Malformed DKIM-Signature header: missing signature parameter."); while (index < rawValue.Length) rawValue[length++] = rawValue[index++]; Array.Resize (ref rawValue, length); return new Header (dkimSignature.Options, dkimSignature.RawField, rawValue); }
static void DkimWriteHeaderSimple (FormatOptions options, Stream stream, Header header) { var rawValue = header.GetRawValue (options); stream.Write (header.RawField, 0, header.RawField.Length); stream.Write (new [] { (byte) ':' }, 0, 1); stream.Write (rawValue, 0, rawValue.Length); }
internal static unsafe bool TryParse (ParserOptions options, byte* input, int length, bool strict, out Header header) { byte* inend = input + length; byte* start = input; byte* inptr = input; // find the end of the field name if (strict) { while (inptr < inend && IsAsciiAtom (*inptr)) inptr++; } else { while (inptr < inend && *inptr != (byte) ':' && !IsControl (*inptr)) inptr++; } while (inptr < inend && IsBlank (*inptr)) inptr++; if (inptr == inend || *inptr != ':') { header = null; return false; } var field = new byte[(int) (inptr - start)]; fixed (byte* outbuf = field) { byte* outptr = outbuf; while (start < inptr) *outptr++ = *start++; } inptr++; int count = (int) (inend - inptr); var value = new byte[count]; fixed (byte *outbuf = value) { byte* outptr = outbuf; while (inptr < inend) *outptr++ = *inptr++; } header = new Header (options, field, value); return true; }
static Header GetSignedDkimSignatureHeader (Header dkimSignature) { // modify the raw DKIM-Signature header value by chopping off the signature after the "b=" at the end var rawValue = (byte[]) dkimSignature.RawValue.Clone (); int length = rawValue.Length; // find the last ';' before the b=... while (length > 0 && rawValue[length - 1] != (byte) ';') length--; // skip over any whitespace while (length < rawValue.Length && rawValue[length].IsWhitespace ()) length++; if (length + 1 >= rawValue.Length || (rawValue[length] != (byte) 'b' && rawValue[length + 1] != (byte) '=')) throw new FormatException ("Malformed DKIM-Signature header: signature parameter is not at the end."); // skip over "b=" length += 2; if (length + 2 < rawValue.Length) { rawValue[length++] = (byte) '\r'; rawValue[length++] = (byte) '\n'; } Array.Resize (ref rawValue, length); return new Header (dkimSignature.Options, dkimSignature.RawField, rawValue); }
/// <summary> /// Tries to parse the given input buffer into a new <see cref="MimeKit.Header"/> instance. /// </summary> /// <remarks> /// Parses a header from the supplied buffer starting at the specified index. /// </remarks> /// <returns><c>true</c>, if the header was successfully parsed, <c>false</c> otherwise.</returns> /// <param name="buffer">The input buffer.</param> /// <param name="startIndex">The starting index of the input buffer.</param> /// <param name="header">The parsed header.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="buffer"/> is <c>null</c>. /// </exception> /// <exception cref="System.ArgumentOutOfRangeException"> /// <paramref name="startIndex"/> is out of range. /// </exception> public static bool TryParse (byte[] buffer, int startIndex, out Header header) { int length = buffer.Length - startIndex; return TryParse (ParserOptions.Default, buffer, startIndex, length, out header); }
/// <summary> /// Verify the specified DKIM-Signature header. /// </summary> /// <remarks> /// Verifies the specified DKIM-Signature header. /// </remarks> /// <param name="dkimSignature">The DKIM-Signature header.</param> /// <param name="publicKeyLocator">The public key locator service.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="dkimSignature"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="publicKeyLocator"/> is <c>null</c>.</para> /// </exception> /// <exception cref="System.ArgumentException"> /// <paramref name="dkimSignature"/> is not a DKIM-Signature header. /// </exception> /// <exception cref="System.FormatException"> /// The DKIM-Signature header value is malformed. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> public bool Verify (Header dkimSignature, IDkimPublicKeyLocator publicKeyLocator, CancellationToken cancellationToken = default (CancellationToken)) { return Verify (FormatOptions.Default, dkimSignature, publicKeyLocator, cancellationToken); }
/// <summary> /// Tries to parse the given input buffer into a new <see cref="MimeKit.Header"/> instance. /// </summary> /// <remarks> /// Parses a header from the specified buffer. /// </remarks> /// <returns><c>true</c>, if the header was successfully parsed, <c>false</c> otherwise.</returns> /// <param name="buffer">The input buffer.</param> /// <param name="header">The parsed header.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="buffer"/> is <c>null</c>. /// </exception> public static bool TryParse (byte[] buffer, out Header header) { return TryParse (ParserOptions.Default, buffer, 0, buffer.Length, out header); }
/// <summary> /// Tries to parse the given input buffer into a new <see cref="MimeKit.Header"/> instance. /// </summary> /// <remarks> /// Parses a header from the supplied buffer starting at the given index /// and spanning across the specified number of bytes. /// </remarks> /// <returns><c>true</c>, if the header was successfully parsed, <c>false</c> otherwise.</returns> /// <param name="options">The parser options to use.</param> /// <param name="buffer">The input buffer.</param> /// <param name="startIndex">The starting index of the input buffer.</param> /// <param name="length">The number of bytes in the input buffer to parse.</param> /// <param name="header">The parsed header.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="options"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="buffer"/> is <c>null</c>.</para> /// </exception> /// <exception cref="System.ArgumentOutOfRangeException"> /// <paramref name="startIndex"/> and <paramref name="length"/> do not specify /// a valid range in the byte array. /// </exception> public static bool TryParse (ParserOptions options, byte[] buffer, int startIndex, int length, out Header header) { if (options == null) throw new ArgumentNullException ("options"); if (buffer == null) throw new ArgumentNullException ("buffer"); if (startIndex < 0 || startIndex > buffer.Length) throw new ArgumentOutOfRangeException ("startIndex"); if (length < 0 || length > (buffer.Length - startIndex)) throw new ArgumentOutOfRangeException ("length"); unsafe { fixed (byte* inptr = buffer) { return TryParse (options.Clone (), inptr + startIndex, length, true, out header); } } }
/// <summary> /// Sets the header using the raw value. /// </summary> /// <remarks> /// Sets the header to the specified value without /// calling <see cref="OnHeadersChanged"/>. /// </remarks> /// <param name="name">The name of the header.</param> /// <param name="rawValue">The raw value of the header.</param> protected void SetHeader (string name, byte[] rawValue) { var header = new Header (Headers.Options, name, rawValue); Headers.Changed -= HeadersChanged; try { Headers.Replace (header); } finally { Headers.Changed += HeadersChanged; } }