Exemple #1
0
        /// <summary>
        /// Asynchronously writes the <see cref="MimeKit.MimePart"/> to the specified output stream.
        /// </summary>
        /// <remarks>
        /// Writes the MIME part to the output stream.
        /// </remarks>
        /// <param name="options">The formatting options.</param>
        /// <param name="stream">The output stream.</param>
        /// <param name="contentOnly"><c>true</c> if only the content should be written; otherwise, <c>false</c>.</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="stream"/> is <c>null</c>.</para>
        /// </exception>
        /// <exception cref="System.OperationCanceledException">
        /// The operation was canceled via the cancellation token.
        /// </exception>
        /// <exception cref="System.IO.IOException">
        /// An I/O error occurred.
        /// </exception>
        public override async Task WriteToAsync(FormatOptions options, Stream stream, bool contentOnly, CancellationToken cancellationToken = default(CancellationToken))
        {
            await base.WriteToAsync(options, stream, contentOnly, cancellationToken).ConfigureAwait(false);

            if (Content == null)
            {
                return;
            }

            if (Content.Encoding != encoding)
            {
                if (encoding == ContentEncoding.UUEncode)
                {
                    var begin  = string.Format("begin 0644 {0}", FileName ?? "unknown");
                    var buffer = Encoding.UTF8.GetBytes(begin);

                    await stream.WriteAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);

                    await stream.WriteAsync(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait(false);
                }

                // transcode the content into the desired Content-Transfer-Encoding
                using (var filtered = new FilteredStream(stream)) {
                    filtered.Add(EncoderFilter.Create(encoding));

                    if (encoding != ContentEncoding.Binary)
                    {
                        filtered.Add(options.CreateNewLineFilter(EnsureNewLine));
                    }

                    await Content.DecodeToAsync(filtered, cancellationToken).ConfigureAwait(false);

                    await filtered.FlushAsync(cancellationToken).ConfigureAwait(false);
                }

                if (encoding == ContentEncoding.UUEncode)
                {
                    var buffer = Encoding.ASCII.GetBytes("end");

                    await stream.WriteAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);

                    await stream.WriteAsync(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait(false);
                }
            }
            else if (encoding != ContentEncoding.Binary)
            {
                using (var filtered = new FilteredStream(stream)) {
                    // Note: if we are writing the top-level MimePart, make sure it ends with a new-line so that
                    // MimeMessage.WriteTo() *always* ends with a new-line.
                    filtered.Add(options.CreateNewLineFilter(EnsureNewLine));
                    await Content.WriteToAsync(filtered, cancellationToken).ConfigureAwait(false);

                    await filtered.FlushAsync(cancellationToken).ConfigureAwait(false);
                }
            }
            else
            {
                await Content.WriteToAsync(stream, cancellationToken).ConfigureAwait(false);
            }
        }
Exemple #2
0
        /// <summary>
        /// Writes the <see cref="MimeKit.MimePart"/> to the specified output stream.
        /// </summary>
        /// <param name="options">The formatting options.</param>
        /// <param name="stream">The output stream.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <para><paramref name="options"/> is <c>null</c>.</para>
        /// <para>-or-</para>
        /// <para><paramref name="stream"/> is <c>null</c>.</para>
        /// </exception>
        /// <exception cref="System.OperationCanceledException">
        /// The operation was canceled via the cancellation token.
        /// </exception>
        /// <exception cref="System.IO.IOException">
        /// An I/O error occurred.
        /// </exception>
        public override void WriteTo(FormatOptions options, Stream stream, CancellationToken cancellationToken)
        {
            base.WriteTo(options, stream, cancellationToken);

            if (ContentObject == null)
            {
                return;
            }

            if (ContentObject.Encoding != encoding)
            {
                if (encoding == ContentEncoding.UUEncode)
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    var begin  = string.Format("begin 0644 {0}", FileName ?? "unknown");
                    var buffer = Encoding.UTF8.GetBytes(begin);
                    stream.Write(buffer, 0, buffer.Length);
                    stream.Write(options.NewLineBytes, 0, options.NewLineBytes.Length);
                }

                // transcode the content into the desired Content-Transfer-Encoding
                using (var filtered = new FilteredStream(stream)) {
                    filtered.Add(EncoderFilter.Create(encoding));

                    if (encoding != ContentEncoding.Binary)
                    {
                        filtered.Add(options.CreateNewLineFilter());
                    }

                    ContentObject.DecodeTo(filtered, cancellationToken);
                    filtered.Flush();
                }

                if (encoding == ContentEncoding.UUEncode)
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    var buffer = Encoding.ASCII.GetBytes("end");
                    stream.Write(buffer, 0, buffer.Length);
                    stream.Write(options.NewLineBytes, 0, options.NewLineBytes.Length);
                }
            }
            else if (encoding != ContentEncoding.Binary)
            {
                using (var filtered = new FilteredStream(stream)) {
                    filtered.Add(options.CreateNewLineFilter());
                    ContentObject.WriteTo(filtered, cancellationToken);
                    filtered.Flush();
                }
            }
            else
            {
                ContentObject.WriteTo(stream, cancellationToken);
            }
        }
Exemple #3
0
        /// <summary>
        /// Writes the <see cref="MimeKit.HeaderList"/> to the specified output stream.
        /// </summary>
        /// <remarks>
        /// Writes all of the headers to the output stream.
        /// </remarks>
        /// <param name="options">The formatting options.</param>
        /// <param name="stream">The output stream.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <para><paramref name="options"/> is <c>null</c>.</para>
        /// <para>-or-</para>
        /// <para><paramref name="stream"/> is <c>null</c>.</para>
        /// </exception>
        /// <exception cref="System.OperationCanceledException">
        /// The operation was canceled via the cancellation token.
        /// </exception>
        /// <exception cref="System.IO.IOException">
        /// An I/O error occurred.
        /// </exception>
        public void WriteTo(FormatOptions options, Stream stream, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (options == null)
            {
                throw new ArgumentNullException("options");
            }

            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }

            cancellationToken.ThrowIfCancellationRequested();

            using (var filtered = new FilteredStream(stream)) {
                filtered.Add(options.CreateNewLineFilter());

                foreach (var header in headers)
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    var name = Encoding.ASCII.GetBytes(header.Field);

                    filtered.Write(name, 0, name.Length);
                    filtered.WriteByte((byte)':');
                    filtered.Write(header.RawValue, 0, header.RawValue.Length);
                }

                filtered.Flush();
            }
        }
Exemple #4
0
        static Task WriteBytesAsync(FormatOptions options, Stream stream, byte[] bytes, bool ensureNewLine, CancellationToken cancellationToken)
        {
            var filter = options.CreateNewLineFilter(ensureNewLine);
            int index, length;

            var output = filter.Flush(bytes, 0, bytes.Length, out index, out length);

            return(stream.WriteAsync(output, index, length, cancellationToken));
        }
Exemple #5
0
        static void WriteBytes(FormatOptions options, Stream stream, byte[] bytes)
        {
            var filter = options.CreateNewLineFilter();
            int index, length;

            var output = filter.Flush(bytes, 0, bytes.Length, out index, out length);

            stream.Write(output, index, length);
        }
Exemple #6
0
        /// <summary>
        /// Writes the message to the specified stream.
        /// </summary>
        /// <param name="options">The formatting options.</param>
        /// <param name="stream">The stream.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <para><paramref name="options"/> is <c>null</c>.</para>
        /// <para>-or-</para>
        /// <para><paramref name="stream"/> is <c>null</c>.</para>
        /// </exception>
        public void WriteTo(FormatOptions options, Stream stream)
        {
            if (options == null)
            {
                throw new ArgumentNullException("options");
            }

            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }

            if (!Headers.Contains("Date"))
            {
                Date = DateTimeOffset.Now;
            }

            if (messageId == null)
            {
                MessageId = MimeUtils.GenerateMessageId();
            }

            if (version == null && Body != null && Body.Headers.Count > 0)
            {
                MimeVersion = new Version(1, 0);
            }

            if (Body == null)
            {
                Headers.WriteTo(stream);

                stream.Write(options.NewLineBytes, 0, options.NewLineBytes.Length);
            }
            else
            {
                using (var filtered = new FilteredStream(stream)) {
                    filtered.Add(options.CreateNewLineFilter());

                    foreach (var header in MergeHeaders())
                    {
                        var name = Encoding.ASCII.GetBytes(header.Field);

                        filtered.Write(name, 0, name.Length);
                        filtered.WriteByte((byte)':');
                        filtered.Write(header.RawValue, 0, header.RawValue.Length);
                    }

                    filtered.Flush();
                }

                options.WriteHeaders = false;
                Body.WriteTo(options, stream);
            }
        }
Exemple #7
0
        static void WriteBytes(FormatOptions options, Stream stream, byte[] bytes, bool ensureNewLine, CancellationToken cancellationToken)
        {
            var cancellable = stream as ICancellableStream;
            var filter = options.CreateNewLineFilter(ensureNewLine);
            int index, length;

            var output = filter.Flush(bytes, 0, bytes.Length, out index, out length);

            if (cancellable != null)
            {
                cancellable.Write(output, index, length, cancellationToken);
            }
            else
            {
                cancellationToken.ThrowIfCancellationRequested();
                stream.Write(output, index, length);
            }
        }
Exemple #8
0
        /// <summary>
        /// Writes the <see cref="MimeKit.HeaderList"/> to the specified output stream.
        /// </summary>
        /// <remarks>
        /// Writes all of the headers to the output stream.
        /// </remarks>
        /// <param name="options">The formatting options.</param>
        /// <param name="stream">The output stream.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <para><paramref name="options"/> is <c>null</c>.</para>
        /// <para>-or-</para>
        /// <para><paramref name="stream"/> is <c>null</c>.</para>
        /// </exception>
        /// <exception cref="System.OperationCanceledException">
        /// The operation was canceled via the cancellation token.
        /// </exception>
        /// <exception cref="System.IO.IOException">
        /// An I/O error occurred.
        /// </exception>
        public void WriteTo(FormatOptions options, Stream stream, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (options == null)
            {
                throw new ArgumentNullException("options");
            }

            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }

            using (var filtered = new FilteredStream(stream)) {
                if (options.NewLineFormat != FormatOptions.Default.NewLineFormat)
                {
                    filtered.Add(options.CreateNewLineFilter());
                }

                foreach (var header in headers)
                {
                    var    name = Encoding.ASCII.GetBytes(header.Field);
                    byte[] rawValue;

                    filtered.Write(name, 0, name.Length, cancellationToken);
                    filtered.Write(new [] { (byte)':' }, 0, 1, cancellationToken);

                    if (options.International)
                    {
                        rawValue = header.GetRawValue(options, Encoding.UTF8);
                    }
                    else
                    {
                        rawValue = header.RawValue;
                    }

                    filtered.Write(rawValue, 0, rawValue.Length, cancellationToken);
                }

                filtered.Flush(cancellationToken);
            }
        }
Exemple #9
0
        /// <summary>
        /// Writes the <see cref="MimeKit.MimePart"/> to the specified output stream.
        /// </summary>
        /// <remarks>
        /// Writes the MIME part to the output stream.
        /// </remarks>
        /// <param name="options">The formatting options.</param>
        /// <param name="stream">The output stream.</param>
        /// <param name="contentOnly"><c>true</c> if only the content should be written; otherwise, <c>false</c>.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <para><paramref name="options"/> is <c>null</c>.</para>
        /// <para>-or-</para>
        /// <para><paramref name="stream"/> is <c>null</c>.</para>
        /// </exception>
        /// <exception cref="System.OperationCanceledException">
        /// The operation was canceled via the cancellation token.
        /// </exception>
        /// <exception cref="System.IO.IOException">
        /// An I/O error occurred.
        /// </exception>
        public override void WriteTo(FormatOptions options, Stream stream, bool contentOnly, CancellationToken cancellationToken = default(CancellationToken))
        {
            base.WriteTo(options, stream, contentOnly, cancellationToken);

            if (ContentObject == null)
            {
                return;
            }

            var cancellable = stream as ICancellableStream;

            if (ContentObject.Encoding != encoding)
            {
                if (encoding == ContentEncoding.UUEncode)
                {
                    var begin  = string.Format("begin 0644 {0}", FileName ?? "unknown");
                    var buffer = Encoding.UTF8.GetBytes(begin);

                    if (cancellable != null)
                    {
                        cancellable.Write(buffer, 0, buffer.Length, cancellationToken);
                        cancellable.Write(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);
                    }
                    else
                    {
                        cancellationToken.ThrowIfCancellationRequested();
                        stream.Write(buffer, 0, buffer.Length);
                        stream.Write(options.NewLineBytes, 0, options.NewLineBytes.Length);
                    }
                }

                // transcode the content into the desired Content-Transfer-Encoding
                using (var filtered = new FilteredStream(stream)) {
                    filtered.Add(EncoderFilter.Create(encoding));

                    if (encoding != ContentEncoding.Binary)
                    {
                        filtered.Add(options.CreateNewLineFilter(true));
                    }

                    ContentObject.DecodeTo(filtered, cancellationToken);
                    filtered.Flush(cancellationToken);
                }

                if (encoding == ContentEncoding.UUEncode)
                {
                    var buffer = Encoding.ASCII.GetBytes("end");

                    if (cancellable != null)
                    {
                        cancellable.Write(buffer, 0, buffer.Length, cancellationToken);
                        cancellable.Write(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);
                    }
                    else
                    {
                        cancellationToken.ThrowIfCancellationRequested();
                        stream.Write(buffer, 0, buffer.Length);
                        stream.Write(options.NewLineBytes, 0, options.NewLineBytes.Length);
                    }
                }
            }
            else if (encoding != ContentEncoding.Binary)
            {
                using (var filtered = new FilteredStream(stream)) {
                    // Note: if we are writing the top-level MimePart, make sure it ends with a new-line so that
                    // MimeMessage.WriteTo() *always* ends with a new-line.
                    filtered.Add(options.CreateNewLineFilter(EnsureNewLine));
                    ContentObject.WriteTo(filtered, cancellationToken);
                    filtered.Flush(cancellationToken);
                }
            }
            else
            {
                ContentObject.WriteTo(stream, cancellationToken);
            }
        }
Exemple #10
0
		/// <summary>
		/// Writes the message to the specified output stream.
		/// </summary>
		/// <remarks>
		/// Writes the message to the output stream using the provided formatting options.
		/// </remarks>
		/// <param name="options">The formatting options.</param>
		/// <param name="stream">The output stream.</param>
		/// <param name="cancellationToken">A cancellation token.</param>
		/// <exception cref="System.ArgumentNullException">
		/// <para><paramref name="options"/> is <c>null</c>.</para>
		/// <para>-or-</para>
		/// <para><paramref name="stream"/> is <c>null</c>.</para>
		/// </exception>
		/// <exception cref="System.OperationCanceledException">
		/// The operation was canceled via the cancellation token.
		/// </exception>
		/// <exception cref="System.IO.IOException">
		/// An I/O error occurred.
		/// </exception>
		public void WriteTo (FormatOptions options, Stream stream, CancellationToken cancellationToken = default (CancellationToken))
		{
			if (options == null)
				throw new ArgumentNullException ("options");

			if (stream == null)
				throw new ArgumentNullException ("stream");

			if (version == null && Body != null && Body.Headers.Count > 0)
				MimeVersion = new Version (1, 0);

			if (Body != null) {
				using (var filtered = new FilteredStream (stream)) {
					filtered.Add (options.CreateNewLineFilter ());

					foreach (var header in MergeHeaders ()) {
						if (options.HiddenHeaders.Contains (header.Id))
							continue;

						filtered.Write (header.RawField, 0, header.RawField.Length, cancellationToken);
						filtered.Write (new [] { (byte) ':' }, 0, 1, cancellationToken);
						filtered.Write (header.RawValue, 0, header.RawValue.Length, cancellationToken);
					}

					filtered.Flush (cancellationToken);
				}

				var cancellable = stream as ICancellableStream;

				if (cancellable != null) {
					cancellable.Write (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);
				} else {
					cancellationToken.ThrowIfCancellationRequested ();
					stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length);
				}

				try {
					Body.Headers.Suppress = true;
					Body.WriteTo (options, stream, cancellationToken);
				} finally {
					Body.Headers.Suppress = false;
				}
			} else {
				Headers.WriteTo (options, stream, cancellationToken);
			}
		}
Exemple #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);
			}
		}
Exemple #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);
			}
		}
Exemple #13
0
		byte[] DkimHashBody (FormatOptions options, DkimSignatureAlgorithm signatureAlgorithm, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm, int maxLength)
		{
			using (var stream = new DkimHashStream (signatureAlgorithm, maxLength)) {
				using (var filtered = new FilteredStream (stream)) {
					filtered.Add (options.CreateNewLineFilter ());

					if (bodyCanonicalizationAlgorithm == DkimCanonicalizationAlgorithm.Relaxed)
						filtered.Add (new DkimRelaxedBodyFilter ());
					else
						filtered.Add (new DkimSimpleBodyFilter ());

					if (Body != null) {
						try {
							Body.Headers.Suppress = true;
							Body.WriteTo (options, stream, CancellationToken.None);
						} finally {
							Body.Headers.Suppress = false;
						}
					}

					filtered.Flush ();
				}

				return stream.GenerateHash ();
			}
		}
Exemple #14
0
        /// <summary>
        /// Writes the message to the specified stream.
        /// </summary>
        /// <param name="options">The formatting options.</param>
        /// <param name="stream">The stream.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <para><paramref name="options"/> is <c>null</c>.</para>
        /// <para>-or-</para>
        /// <para><paramref name="stream"/> is <c>null</c>.</para>
        /// </exception>
        public void WriteTo(FormatOptions options, Stream stream)
        {
            if (options == null)
                throw new ArgumentNullException ("options");

            if (stream == null)
                throw new ArgumentNullException ("stream");

            if (!Headers.Contains ("Date"))
                Date = DateTimeOffset.Now;

            if (messageId == null)
                MessageId = MimeUtils.GenerateMessageId ();

            if (version == null && Body != null && Body.Headers.Count > 0)
                MimeVersion = new Version (1, 0);

            if (Body == null) {
                Headers.WriteTo (stream);

                stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length);
            } else {
                using (var filtered = new FilteredStream (stream)) {
                    filtered.Add (options.CreateNewLineFilter ());

                    foreach (var header in MergeHeaders ()) {
                        var name = Encoding.ASCII.GetBytes (header.Field);

                        filtered.Write (name, 0, name.Length);
                        filtered.WriteByte ((byte) ':');
                        filtered.Write (header.RawValue, 0, header.RawValue.Length);
                    }

                    filtered.Flush ();
                }

                options.WriteHeaders = false;
                Body.WriteTo (options, stream);
            }
        }
Exemple #15
0
        /// <summary>
        /// Asynchronously write the <see cref="MimePart"/> to the specified output stream.
        /// </summary>
        /// <remarks>
        /// Asynchronously writes the MIME part to the output stream.
        /// </remarks>
        /// <returns>An awaitable task.</returns>
        /// <param name="options">The formatting options.</param>
        /// <param name="stream">The output stream.</param>
        /// <param name="contentOnly"><c>true</c> if only the content should be written; otherwise, <c>false</c>.</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="stream"/> is <c>null</c>.</para>
        /// </exception>
        /// <exception cref="System.OperationCanceledException">
        /// The operation was canceled via the cancellation token.
        /// </exception>
        /// <exception cref="System.IO.IOException">
        /// An I/O error occurred.
        /// </exception>
        public override async Task WriteToAsync(FormatOptions options, Stream stream, bool contentOnly, CancellationToken cancellationToken = default(CancellationToken))
        {
            await base.WriteToAsync(options, stream, contentOnly, cancellationToken).ConfigureAwait(false);

            if (Content == null)
            {
                return;
            }

            var isText = ContentType.IsMimeType("text", "*") || ContentType.IsMimeType("message", "*");

            if (Content.Encoding != encoding)
            {
                if (encoding == ContentEncoding.UUEncode)
                {
                    var begin  = string.Format("begin 0644 {0}", FileName ?? "unknown");
                    var buffer = Encoding.UTF8.GetBytes(begin);

                    await stream.WriteAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);

                    await stream.WriteAsync(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait(false);
                }

                // transcode the content into the desired Content-Transfer-Encoding
                using (var filtered = new FilteredStream(stream)) {
                    filtered.Add(EncoderFilter.Create(encoding));

                    if (encoding != ContentEncoding.Binary)
                    {
                        filtered.Add(options.CreateNewLineFilter(EnsureNewLine));
                    }

                    await Content.DecodeToAsync(filtered, cancellationToken).ConfigureAwait(false);

                    await filtered.FlushAsync(cancellationToken).ConfigureAwait(false);
                }

                if (encoding == ContentEncoding.UUEncode)
                {
                    var buffer = Encoding.ASCII.GetBytes("end");

                    await stream.WriteAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);

                    await stream.WriteAsync(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait(false);
                }
            }
            else if (encoding == ContentEncoding.Binary)
            {
                // Do not alter binary content.
                await Content.WriteToAsync(stream, cancellationToken).ConfigureAwait(false);
            }
            else if (options.VerifyingSignature && Content.NewLineFormat.HasValue && Content.NewLineFormat.Value == NewLineFormat.Mixed)
            {
                // Allow pass-through of the original parsed content without canonicalization when verifying signatures
                // if the content contains a mix of line-endings.
                //
                // See https://github.com/jstedfast/MimeKit/issues/569 for details.
                await Content.WriteToAsync(stream, cancellationToken).ConfigureAwait(false);
            }
            else
            {
                using (var filtered = new FilteredStream(stream)) {
                    // Note: if we are writing the top-level MimePart, make sure it ends with a new-line so that
                    // MimeMessage.WriteTo() *always* ends with a new-line.
                    filtered.Add(options.CreateNewLineFilter(EnsureNewLine));
                    await Content.WriteToAsync(filtered, cancellationToken).ConfigureAwait(false);

                    await filtered.FlushAsync(cancellationToken).ConfigureAwait(false);
                }
            }
        }
Exemple #16
0
		byte[] DkimHashBody (FormatOptions options, DkimSignatureAlgorithm signatureAlgorithm, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm, int maxLength)
		{
			using (var stream = new DkimHashStream (signatureAlgorithm, maxLength)) {
				using (var filtered = new FilteredStream (stream)) {
					DkimBodyFilter dkim;

					if (bodyCanonicalizationAlgorithm == DkimCanonicalizationAlgorithm.Relaxed)
						dkim = new DkimRelaxedBodyFilter ();
					else
						dkim = new DkimSimpleBodyFilter ();

					filtered.Add (options.CreateNewLineFilter ());
					filtered.Add (dkim);

					if (Body != null) {
						try {
							Body.EnsureNewLine = compliance == RfcComplianceMode.Strict;
							Body.Headers.Suppress = true;
							Body.WriteTo (options, filtered, CancellationToken.None);
						} finally {
							Body.Headers.Suppress = false;
							Body.EnsureNewLine = false;
						}
					}

					filtered.Flush ();

					if (!dkim.LastWasNewLine)
						stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length);
				}

				return stream.GenerateHash ();
			}
		}
Exemple #17
0
        /// <summary>
        /// Writes the <see cref="MimeKit.MimePart"/> to the specified stream.
        /// </summary>
        /// <param name="options">The formatting options.</param>
        /// <param name="stream">The stream.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <para><paramref name="options"/> is <c>null</c>.</para>
        /// <para>-or-</para>
        /// <para><paramref name="stream"/> is <c>null</c>.</para>
        /// </exception>
        public override void WriteTo(FormatOptions options, Stream stream)
        {
            base.WriteTo (options, stream);

            if (ContentObject == null)
                return;

            if (ContentObject.Encoding != encoding) {
                if (encoding == ContentEncoding.UUEncode) {
                    var begin = string.Format ("begin 0644 {0}", FileName ?? "unknown");
                    var buffer = Encoding.UTF8.GetBytes (begin);
                    stream.Write (buffer, 0, buffer.Length);
                    stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length);
                }

                // transcode the content into the desired Content-Transfer-Encoding
                using (var filtered = new FilteredStream (stream)) {
                    filtered.Add (EncoderFilter.Create (encoding));
                    filtered.Add (options.CreateNewLineFilter ());
                    ContentObject.DecodeTo (filtered);
                    filtered.Flush ();
                }

                if (encoding == ContentEncoding.UUEncode) {
                    var buffer = Encoding.ASCII.GetBytes ("end");
                    stream.Write (buffer, 0, buffer.Length);
                    stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length);
                }
            } else {
                using (var filtered = new FilteredStream (stream)) {
                    filtered.Add (options.CreateNewLineFilter ());
                    ContentObject.WriteTo (filtered);
                    filtered.Flush ();
                }
            }
        }
Exemple #18
0
		/// <summary>
		/// Writes the <see cref="MimeKit.MimePart"/> to the specified output stream.
		/// </summary>
		/// <remarks>
		/// Writes the MIME part to the output stream.
		/// </remarks>
		/// <param name="options">The formatting options.</param>
		/// <param name="stream">The output stream.</param>
		/// <param name="cancellationToken">A cancellation token.</param>
		/// <exception cref="System.ArgumentNullException">
		/// <para><paramref name="options"/> is <c>null</c>.</para>
		/// <para>-or-</para>
		/// <para><paramref name="stream"/> is <c>null</c>.</para>
		/// </exception>
		/// <exception cref="System.OperationCanceledException">
		/// The operation was canceled via the cancellation token.
		/// </exception>
		/// <exception cref="System.IO.IOException">
		/// An I/O error occurred.
		/// </exception>
		public override void WriteTo (FormatOptions options, Stream stream, CancellationToken cancellationToken = default (CancellationToken))
		{
			base.WriteTo (options, stream, cancellationToken);

			if (ContentObject == null)
				return;

			var cancellable = stream as ICancellableStream;

			if (ContentObject.Encoding != encoding) {
				if (encoding == ContentEncoding.UUEncode) {
					var begin = string.Format ("begin 0644 {0}", FileName ?? "unknown");
					var buffer = Encoding.UTF8.GetBytes (begin);

					if (cancellable != null) {
						cancellable.Write (buffer, 0, buffer.Length, cancellationToken);
						cancellable.Write (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);
					} else {
						cancellationToken.ThrowIfCancellationRequested ();
						stream.Write (buffer, 0, buffer.Length);
						stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length);
					}
				}

				// transcode the content into the desired Content-Transfer-Encoding
				using (var filtered = new FilteredStream (stream)) {
					filtered.Add (EncoderFilter.Create (encoding));

					if (encoding != ContentEncoding.Binary)
						filtered.Add (options.CreateNewLineFilter (true));

					ContentObject.DecodeTo (filtered, cancellationToken);
					filtered.Flush (cancellationToken);
				}

				if (encoding == ContentEncoding.UUEncode) {
					var buffer = Encoding.ASCII.GetBytes ("end");

					if (cancellable != null) {
						cancellable.Write (buffer, 0, buffer.Length, cancellationToken);
						cancellable.Write (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);
					} else {
						cancellationToken.ThrowIfCancellationRequested ();
						stream.Write (buffer, 0, buffer.Length);
						stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length);
					}
				}
			} else if (encoding != ContentEncoding.Binary) {
				using (var filtered = new FilteredStream (stream)) {
					// Note: if we are writing the top-level MimePart, make sure it ends with a new-line so that
					// MimeMessage.WriteTo() *always* ends with a new-line.
					filtered.Add (options.CreateNewLineFilter (Headers.Suppress));
					ContentObject.WriteTo (filtered, cancellationToken);
					filtered.Flush (cancellationToken);
				}
			} else {
				ContentObject.WriteTo (stream, cancellationToken);
			}
		}
		/// <summary>
		/// Writes the message to the specified output stream.
		/// </summary>
		/// <remarks>
		/// Writes the message to the output stream using the provided formatting options.
		/// </remarks>
		/// <param name="options">The formatting options.</param>
		/// <param name="stream">The output stream.</param>
		/// <param name="cancellationToken">A cancellation token.</param>
		/// <exception cref="System.ArgumentNullException">
		/// <para><paramref name="options"/> is <c>null</c>.</para>
		/// <para>-or-</para>
		/// <para><paramref name="stream"/> is <c>null</c>.</para>
		/// </exception>
		/// <exception cref="System.OperationCanceledException">
		/// The operation was canceled via the cancellation token.
		/// </exception>
		/// <exception cref="System.IO.IOException">
		/// An I/O error occurred.
		/// </exception>
		public void WriteTo (FormatOptions options, Stream stream, CancellationToken cancellationToken = default (CancellationToken))
		{
			if (options == null)
				throw new ArgumentNullException ("options");

			if (stream == null)
				throw new ArgumentNullException ("stream");

			if (version == null && Body != null && Body.Headers.Count > 0)
				MimeVersion = new Version (1, 0);

			if (Body == null) {
				Headers.WriteTo (options, stream, cancellationToken);

				cancellationToken.ThrowIfCancellationRequested ();
				stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length);
			} else {
				cancellationToken.ThrowIfCancellationRequested ();

				using (var filtered = new FilteredStream (stream)) {
					filtered.Add (options.CreateNewLineFilter ());

					foreach (var header in MergeHeaders ()) {
						if (options.HiddenHeaders.Contains (header.Id))
							continue;

						cancellationToken.ThrowIfCancellationRequested ();

						var name = Encoding.ASCII.GetBytes (header.Field);

						filtered.Write (name, 0, name.Length);
						filtered.WriteByte ((byte) ':');
						filtered.Write (header.RawValue, 0, header.RawValue.Length);
					}

					filtered.Flush ();
				}

				options.WriteHeaders = false;
				Body.WriteTo (options, stream, cancellationToken);
			}
		}