Exemplo n.º 1
0
        /// <summary>
        /// Write the <see cref="MessagePart"/> to the output stream.
        /// </summary>
        /// <remarks>
        /// Writes the MIME entity and its message 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 void WriteTo(FormatOptions options, Stream stream, bool contentOnly, CancellationToken cancellationToken = default(CancellationToken))
        {
            base.WriteTo(options, stream, contentOnly, cancellationToken);

            if (Message == null)
            {
                return;
            }

            if (Message.MboxMarker != null && Message.MboxMarker.Length != 0)
            {
                var cancellable = stream as ICancellableStream;

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

            if (options.EnsureNewLine)
            {
                options = options.Clone();
                options.EnsureNewLine = false;
            }

            Message.WriteTo(options, stream, cancellationToken);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Writes the <see cref="MimeKit.Multipart"/> to the specified output stream.
        /// </summary>
        /// <remarks>
        /// Writes the multipart MIME entity and its subparts 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))
        {
            if (Boundary == null)
            {
                Boundary = GenerateBoundary();
            }

            base.WriteTo(options, stream, cancellationToken);

            if (options.International && ContentType.Matches("multipart", "signed"))
            {
                // don't reformat the headers or content of any children of a multipart/signed
                options = options.Clone();
                options.HiddenHeaders.Clear();
                options.International = false;
            }

            var cancellable = stream as ICancellableStream;

            if (RawPreamble != null && RawPreamble.Length > 0)
            {
                WriteBytes(options, stream, RawPreamble, cancellationToken);
            }

            var boundary = Encoding.ASCII.GetBytes("--" + Boundary + "--");

            if (cancellable != null)
            {
                for (int i = 0; i < children.Count; i++)
                {
                    cancellable.Write(boundary, 0, boundary.Length - 2, cancellationToken);
                    cancellable.Write(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);
                    children[i].WriteTo(options, stream, cancellationToken);
                    cancellable.Write(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);
                }

                cancellable.Write(boundary, 0, boundary.Length, cancellationToken);
                cancellable.Write(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);
            }
            else
            {
                for (int i = 0; i < children.Count; i++)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    stream.Write(boundary, 0, boundary.Length - 2);
                    stream.Write(options.NewLineBytes, 0, options.NewLineBytes.Length);
                    children[i].WriteTo(options, stream, cancellationToken);
                    stream.Write(options.NewLineBytes, 0, options.NewLineBytes.Length);
                }

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

            if (RawEpilogue != null && RawEpilogue.Length > 0)
            {
                WriteBytes(options, stream, RawEpilogue, cancellationToken);
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Asynchronously write the <see cref="MessagePart"/> to the output stream.
        /// </summary>
        /// <remarks>
        /// Asynchronously writes the MIME entity and its message 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 (Message == null)
            {
                return;
            }

            if (Message.MboxMarker != null && Message.MboxMarker.Length != 0)
            {
                await stream.WriteAsync(Message.MboxMarker, 0, Message.MboxMarker.Length, cancellationToken).ConfigureAwait(false);

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

            if (options.EnsureNewLine)
            {
                options = options.Clone();
                options.EnsureNewLine = false;
            }

            await Message.WriteToAsync(options, stream, cancellationToken).ConfigureAwait(false);
        }
Exemplo n.º 4
0
		/// <summary>
		/// Initializes a new instance of the <see cref="MailKit.Net.Imap.ImapLiteral"/> class.
		/// </summary>
		/// <remarks>
		/// Creates a new <see cref="MailKit.Net.Imap.ImapLiteral"/>.
		/// </remarks>
		/// <param name="options">The formatting options.</param>
		/// <param name="literal">The literal.</param>
		/// <param name="action">The progress update action.</param>
		public ImapLiteral (FormatOptions options, object literal, Action<int> action = null)
		{
			format = options.Clone ();
			format.NewLineFormat = NewLineFormat.Dos;

			update = action;

			if (literal is MimeMessage) {
				Type = ImapLiteralType.MimeMessage;
			} else if (literal is Stream) {
				Type = ImapLiteralType.Stream;
			} else if (literal is string) {
				literal = Encoding.UTF8.GetBytes ((string) literal);
				Type = ImapLiteralType.String;
			} else if (literal is byte[]) {
				Type = ImapLiteralType.String;
			} else {
				throw new ArgumentException ("Unknown literal type");
			}

			Literal = literal;
		}
Exemplo n.º 5
0
 /// <summary>
 /// Get the default formatting options in a thread-safe way.
 /// </summary>
 /// <remarks>
 /// Gets the default formatting options in a thread-safe way.
 /// </remarks>
 /// <returns>The default formatting options.</returns>
 internal static FormatOptions CloneDefault()
 {
     lock (Default) {
         return(Default.Clone());
     }
 }
Exemplo n.º 6
0
		/// <summary>
		/// Appends the specified messages to the folder.
		/// </summary>
		/// <remarks>
		/// Appends the specified messages to the folder and returns the UniqueIds assigned to the messages.
		/// </remarks>
		/// <returns>The UIDs of the appended messages, if available; otherwise an empty array.</returns>
		/// <param name="options">The formatting options.</param>
		/// <param name="messages">The list of messages to append to the folder.</param>
		/// <param name="flags">The message flags to use for each of the messages.</param>
		/// <param name="dates">The received dates to use for each of the messages.</param>
		/// <param name="cancellationToken">The cancellation token.</param>
		/// <param name="progress">The progress reporting mechanism.</param>
		/// <exception cref="System.ArgumentNullException">
		/// <para><paramref name="options"/> is <c>null</c>.</para>
		/// <para>-or-</para>
		/// <para><paramref name="messages"/> is <c>null</c>.</para>
		/// <para>-or-</para>
		/// <para><paramref name="flags"/> is <c>null</c>.</para>
		/// <para>-or-</para>
		/// <para><paramref name="dates"/> is <c>null</c>.</para>
		/// </exception>
		/// <exception cref="System.ArgumentException">
		/// <para>One or more of the <paramref name="messages"/> is null.</para>
		/// <para>-or-</para>
		/// <para>The number of messages, flags, and dates do not match.</para>
		/// </exception>
		/// <exception cref="System.ObjectDisposedException">
		/// The <see cref="ImapClient"/> has been disposed.
		/// </exception>
		/// <exception cref="ServiceNotConnectedException">
		/// The <see cref="ImapClient"/> is not connected.
		/// </exception>
		/// <exception cref="ServiceNotAuthenticatedException">
		/// The <see cref="ImapClient"/> is not authenticated.
		/// </exception>
		/// <exception cref="System.InvalidOperationException">
		/// Internationalized formatting was requested but has not been enabled.
		/// </exception>
		/// <exception cref="FolderNotFoundException">
		/// The <see cref="ImapFolder"/> does not exist.
		/// </exception>
		/// <exception cref="System.OperationCanceledException">
		/// The operation was canceled via the cancellation token.
		/// </exception>
		/// <exception cref="System.NotSupportedException">
		/// Internationalized formatting was requested but is not supported by the server.
		/// </exception>
		/// <exception cref="System.IO.IOException">
		/// An I/O error occurred.
		/// </exception>
		/// <exception cref="ImapProtocolException">
		/// The server's response contained unexpected tokens.
		/// </exception>
		/// <exception cref="ImapCommandException">
		/// The server replied with a NO or BAD response.
		/// </exception>
		public override IList<UniqueId> Append (FormatOptions options, IList<MimeMessage> messages, IList<MessageFlags> flags, IList<DateTimeOffset> dates, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null)
		{
			if (options == null)
				throw new ArgumentNullException ("options");

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

			for (int i = 0; i < messages.Count; i++) {
				if (messages[i] == null)
					throw new ArgumentException ("One or more of the messages is null.");
			}

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

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

			if (messages.Count != flags.Count || messages.Count != dates.Count)
				throw new ArgumentException ("The number of messages, the number of flags, and the number of dates must be equal.");

			CheckState (false, false);

			if (options.International && (Engine.Capabilities & ImapCapabilities.UTF8Accept) == 0)
				throw new NotSupportedException ("The IMAP server does not support the UTF8 extension.");

			var format = options.Clone ();
			format.NewLineFormat = NewLineFormat.Dos;

			if ((Engine.Capabilities & ImapCapabilities.UTF8Only) == ImapCapabilities.UTF8Only)
				format.International = true;

			if (format.International && !Engine.UTF8Enabled)
				throw new InvalidOperationException ("The UTF8 extension has not been enabled.");

			if (messages.Count == 0)
				return new UniqueId[0];

			if ((Engine.Capabilities & ImapCapabilities.MultiAppend) != 0) {
				var ic = QueueMultiAppend (format, messages, flags, dates, cancellationToken, progress);

				Engine.Wait (ic);

				ProcessResponseCodes (ic, null);

				if (ic.Response != ImapCommandResponse.Ok)
					throw ImapCommandException.Create ("APPEND", ic);

				var append = ic.RespCodes.OfType<AppendUidResponseCode> ().FirstOrDefault ();

				if (append != null)
					return append.UidSet;

				return new UniqueId[0];
			}

			// FIXME: use an aggregate progress reporter
			var uids = new List<UniqueId> ();

			for (int i = 0; i < messages.Count; i++) {
				var uid = Append (format, messages[i], flags[i], dates[i], cancellationToken);
				if (uids != null && uid.HasValue)
					uids.Add (uid.Value);
				else
					uids = null;
			}

			if (uids == null)
				return new UniqueId[0];

			return uids;
		}
Exemplo n.º 7
0
		/// <summary>
		/// Appends the specified message to the folder.
		/// </summary>
		/// <remarks>
		/// Appends the specified message to the folder and returns the UniqueId assigned to the message.
		/// </remarks>
		/// <returns>The UID of the appended message, if available; otherwise, <c>null</c>.</returns>
		/// <param name="options">The formatting options.</param>
		/// <param name="message">The message.</param>
		/// <param name="flags">The message flags.</param>
		/// <param name="date">The received date of the message.</param>
		/// <param name="cancellationToken">The cancellation token.</param>
		/// <param name="progress">The progress reporting mechanism.</param>
		/// <exception cref="System.ArgumentNullException">
		/// <para><paramref name="options"/> is <c>null</c>.</para>
		/// <para>-or-</para>
		/// <para><paramref name="message"/> is <c>null</c>.</para>
		/// </exception>
		/// <exception cref="System.ObjectDisposedException">
		/// The <see cref="ImapClient"/> has been disposed.
		/// </exception>
		/// <exception cref="ServiceNotConnectedException">
		/// The <see cref="ImapClient"/> is not connected.
		/// </exception>
		/// <exception cref="ServiceNotAuthenticatedException">
		/// The <see cref="ImapClient"/> is not authenticated.
		/// </exception>
		/// <exception cref="System.InvalidOperationException">
		/// Internationalized formatting was requested but has not been enabled.
		/// </exception>
		/// <exception cref="FolderNotFoundException">
		/// The <see cref="ImapFolder"/> does not exist.
		/// </exception>
		/// <exception cref="System.OperationCanceledException">
		/// The operation was canceled via the cancellation token.
		/// </exception>
		/// <exception cref="System.NotSupportedException">
		/// Internationalized formatting was requested but is not supported by the server.
		/// </exception>
		/// <exception cref="System.IO.IOException">
		/// An I/O error occurred.
		/// </exception>
		/// <exception cref="ImapProtocolException">
		/// The server's response contained unexpected tokens.
		/// </exception>
		/// <exception cref="ImapCommandException">
		/// The server replied with a NO or BAD response.
		/// </exception>
		public override UniqueId? Append (FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null)
		{
			if (options == null)
				throw new ArgumentNullException ("options");

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

			CheckState (false, false);

			if (options.International && (Engine.Capabilities & ImapCapabilities.UTF8Accept) == 0)
				throw new NotSupportedException ("The IMAP server does not support the UTF8 extension.");

			var format = options.Clone ();
			format.NewLineFormat = NewLineFormat.Dos;

			if ((Engine.Capabilities & ImapCapabilities.UTF8Only) == ImapCapabilities.UTF8Only)
				format.International = true;

			if (format.International && !Engine.UTF8Enabled)
				throw new InvalidOperationException ("The UTF8 extension has not been enabled.");

			var ic = QueueAppend (format, message, flags, date, cancellationToken, progress);

			Engine.Wait (ic);

			ProcessResponseCodes (ic, this);

			if (ic.Response != ImapCommandResponse.Ok)
				throw ImapCommandException.Create ("APPEND", ic);

			var append = ic.RespCodes.OfType<AppendUidResponseCode> ().FirstOrDefault ();

			if (append != null)
				return append.UidSet[0];

			return null;
		}
Exemplo n.º 8
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);
			}
		}
Exemplo n.º 9
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);
			}
		}
Exemplo n.º 10
0
		void Send (FormatOptions options, MimeMessage message, MailboxAddress sender, IList<MailboxAddress> recipients, CancellationToken cancellationToken, ITransferProgress progress)
		{
			CheckDisposed ();

			if (!IsConnected)
				throw new ServiceNotConnectedException ("The SmtpClient is not connected.");

			var format = options.Clone ();
			format.International = format.International || sender.IsInternational || recipients.Any (x => x.IsInternational);
			format.HiddenHeaders.Add (HeaderId.ContentLength);
			format.HiddenHeaders.Add (HeaderId.ResentBcc);
			format.HiddenHeaders.Add (HeaderId.Bcc);
			format.NewLineFormat = NewLineFormat.Dos;

			if (format.International && (Capabilities & SmtpCapabilities.UTF8) == 0)
				throw new NotSupportedException ("The SMTP server does not support the SMTPUTF8 extension.");

			if (format.International && (Capabilities & SmtpCapabilities.EightBitMime) == 0)
				throw new NotSupportedException ("The SMTP server does not support the 8BITMIME extension.");

			// prepare the message
			if ((Capabilities & SmtpCapabilities.BinaryMime) != 0)
				message.Prepare (EncodingConstraint.None, MaxLineLength);
			else if ((Capabilities & SmtpCapabilities.EightBitMime) != 0)
				message.Prepare (EncodingConstraint.EightBit, MaxLineLength);
			else
				message.Prepare (EncodingConstraint.SevenBit, MaxLineLength);

			// figure out which SMTP extensions we need to use
			var visitor = new ContentTransferEncodingVisitor (capabilities);
			visitor.Visit (message);

			var extensions = visitor.SmtpExtensions;

			if (format.International)
				extensions |= SmtpExtension.UTF8;

			try {
				// Note: if PIPELINING is supported, MailFrom() and RcptTo() will
				// queue their commands instead of sending them immediately.
				MailFrom (message, sender, extensions, cancellationToken);

				var unique = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
				foreach (var recipient in recipients) {
					if (unique.Add (recipient.Address))
						RcptTo (message, recipient, cancellationToken);
				}

				// Note: if PIPELINING is supported, this will flush all outstanding
				// MAIL FROM and RCPT TO commands to the server and then process all
				// of their responses.
				FlushCommandQueue (sender, recipients, cancellationToken);

				if ((extensions & SmtpExtension.BinaryMime) != 0)
					Bdat (format, message, cancellationToken, progress);
				else
					Data (format, message, cancellationToken, progress);
			} catch (ServiceNotAuthenticatedException) {
				// do not disconnect
				throw;
			} catch (SmtpCommandException) {
				Reset (cancellationToken);
				throw;
			} catch {
				Disconnect ();
				throw;
			}
		}
Exemplo n.º 11
0
        /// <summary>
        /// Asynchronously write the <see cref="Multipart"/> to the specified output stream.
        /// </summary>
        /// <remarks>
        /// Asynchronously writes the multipart MIME entity and its subparts 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 (ContentType.IsMimeType("multipart", "signed"))
            {
                // don't hide or reformat the headers of any children of a multipart/signed
                if (options.International || options.HiddenHeaders.Count > 0)
                {
                    options = options.Clone();
                    options.HiddenHeaders.Clear();
                    options.International = false;
                }
            }

            if (RawPreamble != null && RawPreamble.Length > 0)
            {
                await WriteBytesAsync(options, stream, RawPreamble, children.Count > 0 || EnsureNewLine, cancellationToken).ConfigureAwait(false);
            }

            var boundary = Encoding.ASCII.GetBytes("--" + Boundary + "--");

            for (int i = 0; i < children.Count; i++)
            {
                var msg   = children[i] as MessagePart;
                var multi = children[i] as Multipart;
                var part  = children[i] as MimePart;

                await stream.WriteAsync(boundary, 0, boundary.Length - 2, cancellationToken).ConfigureAwait(false);

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

                await children[i].WriteToAsync(options, stream, false, cancellationToken).ConfigureAwait(false);

                if (msg != null && msg.Message != null && msg.Message.Body != null)
                {
                    multi = msg.Message.Body as Multipart;
                    part  = msg.Message.Body as MimePart;
                }

                if ((part != null && part.Content == null) ||
                    (multi != null && !multi.WriteEndBoundary))
                {
                    continue;
                }

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

            if (!WriteEndBoundary)
            {
                return;
            }

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

            if (RawEpilogue == null)
            {
                await stream.WriteAsync(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait(false);
            }

            if (RawEpilogue != null && RawEpilogue.Length > 0)
            {
                await WriteBytesAsync(options, stream, RawEpilogue, EnsureNewLine, cancellationToken).ConfigureAwait(false);
            }
        }
Exemplo n.º 12
0
        /// <summary>
        /// Write the <see cref="Multipart"/> to the specified output stream.
        /// </summary>
        /// <remarks>
        /// Writes the multipart MIME entity and its subparts 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 void WriteTo(FormatOptions options, Stream stream, bool contentOnly, CancellationToken cancellationToken = default(CancellationToken))
        {
            base.WriteTo(options, stream, contentOnly, cancellationToken);

            if (ContentType.IsMimeType("multipart", "signed"))
            {
                // don't reformat the headers or content of any children of a multipart/signed
                if (options.International || options.HiddenHeaders.Count > 0)
                {
                    options = options.Clone();
                    options.HiddenHeaders.Clear();
                    options.International = false;
                }
            }

            var cancellable = stream as ICancellableStream;

            if (RawPreamble != null && RawPreamble.Length > 0)
            {
                WriteBytes(options, stream, RawPreamble, children.Count > 0 || EnsureNewLine, cancellationToken);
            }

            var boundary = Encoding.ASCII.GetBytes("--" + Boundary + "--");

            if (cancellable != null)
            {
                for (int i = 0; i < children.Count; i++)
                {
                    var msg   = children[i] as MessagePart;
                    var multi = children[i] as Multipart;
                    var part  = children[i] as MimePart;

                    cancellable.Write(boundary, 0, boundary.Length - 2, cancellationToken);
                    cancellable.Write(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);
                    children[i].WriteTo(options, stream, false, cancellationToken);

                    if (msg != null && msg.Message != null && msg.Message.Body != null)
                    {
                        multi = msg.Message.Body as Multipart;
                        part  = msg.Message.Body as MimePart;
                    }

                    if ((part != null && part.Content == null) ||
                        (multi != null && !multi.WriteEndBoundary))
                    {
                        continue;
                    }

                    cancellable.Write(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);
                }

                if (!WriteEndBoundary)
                {
                    return;
                }

                cancellable.Write(boundary, 0, boundary.Length, cancellationToken);

                if (RawEpilogue == null)
                {
                    cancellable.Write(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);
                }
            }
            else
            {
                for (int i = 0; i < children.Count; i++)
                {
                    var rfc822 = children[i] as MessagePart;
                    var multi  = children[i] as Multipart;
                    var part   = children[i] as MimePart;

                    cancellationToken.ThrowIfCancellationRequested();
                    stream.Write(boundary, 0, boundary.Length - 2);
                    stream.Write(options.NewLineBytes, 0, options.NewLineBytes.Length);
                    children[i].WriteTo(options, stream, false, cancellationToken);

                    if (rfc822 != null && rfc822.Message != null && rfc822.Message.Body != null)
                    {
                        multi = rfc822.Message.Body as Multipart;
                        part  = rfc822.Message.Body as MimePart;
                    }

                    if ((part != null && part.Content == null) ||
                        (multi != null && !multi.WriteEndBoundary))
                    {
                        continue;
                    }

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

                if (!WriteEndBoundary)
                {
                    return;
                }

                cancellationToken.ThrowIfCancellationRequested();
                stream.Write(boundary, 0, boundary.Length);

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

            if (RawEpilogue != null && RawEpilogue.Length > 0)
            {
                WriteBytes(options, stream, RawEpilogue, EnsureNewLine, cancellationToken);
            }
        }