A class representing a Message or MIME header.
Represents a single header field and value pair.
Example #1
0
		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.");
		}
Example #2
0
		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.");
		}
Example #3
0
        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.");
        }
Example #4
0
		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.");
		}
Example #5
0
        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.");
            }
        }
Example #6
0
		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));
		}
Example #7
0
		/// <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);
		}
Example #8
0
		/// <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);
				}
			}
		}
Example #9
0
		/// <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");
			}
		}
Example #10
0
		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;
		}
Example #11
0
		/// <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);
			}
		}
Example #12
0
		/// <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);
			}
		}
Example #13
0
		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);
		}
Example #14
0
		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);
		}
Example #15
0
        /// <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 ();
            }
        }
Example #16
0
		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);
		}
Example #17
0
		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.");
		}
Example #18
0
		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.");
		}
Example #19
0
		/// <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");
			}
		}
Example #20
0
		/// <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;
		}
Example #21
0
		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));
		}
Example #22
0
		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);
		}
Example #23
0
		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);
		}
Example #24
0
		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;
		}
Example #25
0
		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);
		}
Example #26
0
		/// <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);
		}
Example #27
0
		/// <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);
		}
Example #28
0
		/// <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);
		}
Example #29
0
		/// <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);
				}
			}
		}
Example #30
0
		/// <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;
			}
		}