Esempio n. 1
0
        static FormatOptions()
        {
            Default = new FormatOptions ();
            Default.MaxLineLength = 72;
            Default.WriteHeaders = true;

            if (Environment.NewLine.Length == 1)
                Default.NewLineFormat = NewLineFormat.Unix;
            else
                Default.NewLineFormat = NewLineFormat.Dos;
        }
Esempio n. 2
0
		/// <summary>
		/// Clones an instance of <see cref="MimeKit.FormatOptions"/>.
		/// </summary>
		/// <remarks>
		/// Clones the formatting options.
		/// </remarks>
		/// <returns>An exact copy of the <see cref="FormatOptions"/>.</returns>
		public FormatOptions Clone ()
		{
			var options = new FormatOptions ();
			//options.maxLineLength = maxLineLength;
			options.newLineFormat = newLineFormat;
			options.HiddenHeaders = new HashSet<HeaderId> (HiddenHeaders);
			options.international = international;
			options.WriteHeaders = true;
			return options;
		}
Esempio n. 3
0
		/// <summary>
		/// Initializes a new instance of the <see cref="MailKit.Net.Imap.ImapCommand"/> class.
		/// </summary>
		/// <remarks>
		/// Creates a new <see cref="MailKit.Net.Imap.ImapCommand"/>.
		/// </remarks>
		/// <param name="engine">The IMAP engine that will be sending the command.</param>
		/// <param name="cancellationToken">The cancellation token.</param>
		/// <param name="folder">The IMAP folder that the command operates on.</param>
		/// <param name="options">The formatting options.</param>
		/// <param name="format">The command format.</param>
		/// <param name="args">The command arguments.</param>
		public ImapCommand (ImapEngine engine, CancellationToken cancellationToken, ImapFolder folder, FormatOptions options, string format, params object[] args)
		{
			UntaggedHandlers = new Dictionary<string, ImapUntaggedHandler> ();
			RespCodes = new List<ImapResponseCode> ();
			CancellationToken = cancellationToken;
			Response = ImapCommandResponse.None;
			Status = ImapCommandStatus.Created;
			Engine = engine;
			Folder = folder;

			using (var builder = new MemoryStream ()) {
				var plus = (Engine.Capabilities & ImapCapabilities.LiteralPlus) != 0 ? "+" : string.Empty;
				int argc = 0;
				byte[] buf;
				string str;
				char c;

				for (int i = 0; i < format.Length; i++) {
					if (format[i] == '%') {
						switch (format[++i]) {
						case '%': // a literal %
							builder.WriteByte ((byte) '%');
							break;
						case 'c': // a character
							c = (char) args[argc++];
							builder.WriteByte ((byte) c);
							break;
						case 'd': // an integer
							str = ((int) args[argc++]).ToString ();
							buf = Encoding.ASCII.GetBytes (str);
							builder.Write (buf, 0, buf.Length);
							break;
						case 'u': // an unsigned integer
							str = ((uint) args[argc++]).ToString ();
							buf = Encoding.ASCII.GetBytes (str);
							builder.Write (buf, 0, buf.Length);
							break;
						case 'F': // an ImapFolder
							var utf7 = ((ImapFolder) args[argc++]).EncodedName;
							AppendString (options, true, builder, utf7);
							break;
						case 'L':
							var literal = new ImapLiteral (options, args[argc++], UpdateProgress);
							var length = literal.Length;

							totalSize += length;

							if (options.International)
								str = "UTF8 (~{" + length + plus + "}\r\n";
							else
								str = "{" + length + plus + "}\r\n";

							buf = Encoding.ASCII.GetBytes (str);
							builder.Write (buf, 0, buf.Length);

							parts.Add (new ImapCommandPart (builder.ToArray (), literal));
							builder.SetLength (0);

							if (options.International)
								builder.WriteByte ((byte) ')');
							break;
						case 'S': // a string which may need to be quoted or made into a literal
							AppendString (options, true, builder, (string) args[argc++]);
							break;
						case 'Q': // similar to %S but string must be quoted at a minimum
							AppendString (options, false, builder, (string) args[argc++]);
							break;
						case 's': // a safe atom string
							buf = Encoding.ASCII.GetBytes ((string) args[argc++]);
							builder.Write (buf, 0, buf.Length);
							break;
						default:
							throw new FormatException ();
						}
					} else {
						builder.WriteByte ((byte) format[i]);
					}
				}

				parts.Add (new ImapCommandPart (builder.ToArray (), null));
			}
		}
Esempio n. 4
0
		byte[] FormatRawValue (FormatOptions format, Encoding encoding)
		{
			switch (Id) {
			case HeaderId.DispositionNotificationTo:
			case HeaderId.ResentFrom:
			case HeaderId.ResentBcc:
			case HeaderId.ResentCc:
			case HeaderId.ResentTo:
			case HeaderId.From:
			case HeaderId.Bcc:
			case HeaderId.Cc:
			case HeaderId.To:
				return EncodeAddressHeader (Options, format, encoding, Field, textValue);
			case HeaderId.Received:
				return EncodeReceivedHeader (Options, format, encoding, Field, textValue);
			case HeaderId.ResentMessageId:
			case HeaderId.MessageId:
			case HeaderId.ContentId:
				return EncodeMessageIdHeader (Options, format, encoding, Field, textValue);
			case HeaderId.References:
				return EncodeReferencesHeader (Options, format, encoding, Field, textValue);
			case HeaderId.ContentDisposition:
				return EncodeContentDisposition (Options, format, encoding, Field, textValue);
			case HeaderId.ContentType:
				return EncodeContentType (Options, format, encoding, Field, textValue);
			case HeaderId.DkimSignature:
				return EncodeDkimSignatureHeader (Options, format, encoding, Field, textValue);
			default:
				return EncodeUnstructuredHeader (Options, format, encoding, Field, textValue);
			}
		}
Esempio n. 5
0
        /// <summary>
        /// Writes the <see cref="MimeKit.MessagePart"/> to the 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 (Message != null)
                Message.WriteTo (options, stream, cancellationToken);
        }
Esempio 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;
		}
Esempio n. 7
0
 /// <summary>
 /// Writes the <see cref="MimeKit.HeaderList"/> to the specified output stream.
 /// </summary>
 /// <param name="options">The formatting options.</param>
 /// <param name="stream">The output 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>
 /// <exception cref="System.IO.IOException">
 /// An I/O error occurred.
 /// </exception>
 public void WriteTo(FormatOptions options, Stream stream)
 {
     WriteTo(options, stream, CancellationToken.None);
 }
Esempio n. 8
0
		void DkimWriteHeaders (FormatOptions options, IList<string> fields, DkimCanonicalizationAlgorithm headerCanonicalizationAlgorithm, Stream stream)
		{
			var counts = new Dictionary<string, int> ();
			Header header;

			for (int i = 0; i < fields.Count; i++) {
				var name = fields[i].ToLowerInvariant ();
				int index, count, n = 0;

				if (!counts.TryGetValue (name, out count))
					count = 0;

				// Note: signers choosing to sign an existing header field that occurs more
				// than once in the message (such as Received) MUST sign the physically last
				// instance of that header field in the header block. Signers wishing to sign
				// multiple instances of such a header field MUST include the header field
				// name multiple times in the list of header fields and MUST sign such header
				// fields in order from the bottom of the header field block to the top.
				index = Headers.LastIndexOf (name);

				// find the n'th header with this name
				while (n < count && --index >= 0) {
					if (Headers[index].Field.Equals (name, StringComparison.OrdinalIgnoreCase))
						n++;
				}

				if (index < 0)
					continue;

				header = Headers[index];

				switch (headerCanonicalizationAlgorithm) {
				case DkimCanonicalizationAlgorithm.Relaxed:
					DkimWriteHeaderRelaxed (options, stream, header);
					break;
				default:
					DkimWriteHeaderSimple (options, stream, header);
					break;
				}

				counts[name] = ++count;
			}
		}
Esempio n. 9
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="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 msg   = 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 (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;
                    }

                    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);
            }
        }
Esempio n. 10
0
        /// <summary>
        /// Asynchronously writes the <see cref="MimeKit.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);
            }
        }
Esempio n. 11
0
        internal override void Encode(FormatOptions options, StringBuilder builder, ref int lineLength)
        {
            if (builder == null)
            {
                throw new ArgumentNullException("builder");
            }

            if (lineLength < 0)
            {
                throw new ArgumentOutOfRangeException("lineLength");
            }

            string route = Route.ToString();

            if (!string.IsNullOrEmpty(route))
            {
                route += ":";
            }

            if (!string.IsNullOrEmpty(Name))
            {
                var encoded = Rfc2047.EncodePhrase(options, Encoding, Name);
                var str     = Encoding.ASCII.GetString(encoded);

                if (lineLength + str.Length > options.MaxLineLength)
                {
                    if (str.Length > options.MaxLineLength)
                    {
                        // we need to break up the name...
                        builder.AppendFolded(options, str, ref lineLength);
                    }
                    else
                    {
                        // the name itself is short enough to fit on a single line,
                        // but only if we write it on a line by itself
                        if (lineLength > 1)
                        {
                            builder.LineWrap(options);
                            lineLength = 1;
                        }

                        lineLength += str.Length;
                        builder.Append(str);
                    }
                }
                else
                {
                    // we can safely fit the name on this line...
                    lineLength += str.Length;
                    builder.Append(str);
                }

                if ((lineLength + route.Length + Address.Length + 3) > options.MaxLineLength)
                {
                    builder.Append("\n\t<");
                    lineLength = 2;
                }
                else
                {
                    builder.Append(" <");
                    lineLength += 2;
                }

                lineLength += route.Length;
                builder.Append(route);

                lineLength += Address.Length + 1;
                builder.Append(Address);
                builder.Append('>');
            }
            else if (!string.IsNullOrEmpty(route))
            {
                if ((lineLength + route.Length + Address.Length + 2) > options.MaxLineLength)
                {
                    builder.Append("\n\t<");
                    lineLength = 2;
                }
                else
                {
                    builder.Append('<');
                    lineLength++;
                }

                lineLength += route.Length;
                builder.Append(route);

                lineLength += Address.Length + 1;
                builder.Append(Address);
                builder.Append('>');
            }
            else
            {
                if ((lineLength + Address.Length) > options.MaxLineLength)
                {
                    builder.LineWrap(options);
                    lineLength = 1;
                }

                lineLength += Address.Length;
                builder.Append(Address);
            }
        }
Esempio n. 12
0
        internal override void Encode(FormatOptions options, StringBuilder builder, ref int lineLength)
        {
            var route = Route.Encode(options);

            if (!string.IsNullOrEmpty(route))
            {
                route += ":";
            }

            string addrspec;

            if (options.International)
            {
                addrspec = DecodeAddrspec(address, at);
            }
            else
            {
                addrspec = EncodeAddrspec(address, at);
            }

            if (!string.IsNullOrEmpty(Name))
            {
                string name;

                if (!options.International)
                {
                    var encoded = Rfc2047.EncodePhrase(options, Encoding, Name);
                    name = Encoding.ASCII.GetString(encoded, 0, encoded.Length);
                }
                else
                {
                    name = EncodeInternationalizedPhrase(Name);
                }

                if (lineLength + name.Length > options.MaxLineLength)
                {
                    if (name.Length > options.MaxLineLength)
                    {
                        // we need to break up the name...
                        builder.AppendFolded(options, name, ref lineLength);
                    }
                    else
                    {
                        // the name itself is short enough to fit on a single line,
                        // but only if we write it on a line by itself
                        if (lineLength > 1)
                        {
                            builder.LineWrap(options);
                            lineLength = 1;
                        }

                        lineLength += name.Length;
                        builder.Append(name);
                    }
                }
                else
                {
                    // we can safely fit the name on this line...
                    lineLength += name.Length;
                    builder.Append(name);
                }

                if ((lineLength + route.Length + addrspec.Length + 3) > options.MaxLineLength)
                {
                    builder.Append(options.NewLine);
                    builder.Append("\t<");
                    lineLength = 2;
                }
                else
                {
                    builder.Append(" <");
                    lineLength += 2;
                }

                lineLength += route.Length;
                builder.Append(route);

                lineLength += addrspec.Length + 1;
                builder.Append(addrspec);
                builder.Append('>');
            }
            else if (!string.IsNullOrEmpty(route))
            {
                if ((lineLength + route.Length + addrspec.Length + 2) > options.MaxLineLength)
                {
                    builder.Append(options.NewLine);
                    builder.Append("\t<");
                    lineLength = 2;
                }
                else
                {
                    builder.Append('<');
                    lineLength++;
                }

                lineLength += route.Length;
                builder.Append(route);

                lineLength += addrspec.Length + 1;
                builder.Append(addrspec);
                builder.Append('>');
            }
            else
            {
                if ((lineLength + addrspec.Length) > options.MaxLineLength)
                {
                    builder.LineWrap(options);
                    lineLength = 1;
                }

                lineLength += addrspec.Length;
                builder.Append(addrspec);
            }
        }
Esempio n. 13
0
        internal static string Fold(FormatOptions format, string field, string value)
        {
            var folded     = new StringBuilder(value.Length);
            int lineLength = field.Length + 2;
            int lastLwsp   = -1;

            folded.Append(' ');

            var words = TokenizeText(value);

            foreach (var word in words)
            {
                if (IsWhiteSpace(word[0]))
                {
                    if (lineLength + word.Length > format.MaxLineLength)
                    {
                        for (int i = 0; i < word.Length; i++)
                        {
                            if (lineLength > format.MaxLineLength)
                            {
                                folded.Append(format.NewLine);
                                lineLength = 0;
                            }

                            folded.Append(word[i]);
                            lineLength++;
                        }
                    }
                    else
                    {
                        lineLength += word.Length;
                        folded.Append(word);
                    }

                    lastLwsp = folded.Length - 1;
                    continue;
                }

                if (lastLwsp != -1 && lineLength + word.Length > format.MaxLineLength)
                {
                    folded.Insert(lastLwsp, format.NewLine);
                    lineLength = 1;
                    lastLwsp   = -1;
                }

                if (word.Length > format.MaxLineLength)
                {
                    foreach (var broken in WordBreak(format, word, lineLength))
                    {
                        if (lineLength + broken.Length > format.MaxLineLength)
                        {
                            folded.Append(format.NewLine);
                            folded.Append(' ');
                            lineLength = 1;
                        }

                        folded.Append(broken.Text, broken.StartIndex, broken.Length);
                        lineLength += broken.Length;
                    }
                }
                else
                {
                    lineLength += word.Length;
                    folded.Append(word);
                }
            }

            folded.Append(format.NewLine);

            return(folded.ToString());
        }
Esempio n. 14
0
		/// <summary>
		/// Writes the message to the specified file.
		/// </summary>
		/// <remarks>
		/// Writes the message to the specified file using the provided formatting options.
		/// </remarks>
		/// <param name="options">The formatting options.</param>
		/// <param name="fileName">The file.</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="fileName"/> is <c>null</c>.</para>
		/// </exception>
		/// <exception cref="System.ArgumentException">
		/// <paramref name="fileName"/> is a zero-length string, contains only white space, or
		/// contains one or more invalid characters as defined by
		/// <see cref="System.IO.Path.InvalidPathChars"/>.
		/// </exception>
		/// <exception cref="System.OperationCanceledException">
		/// The operation was canceled via the cancellation token.
		/// </exception>
		/// <exception cref="System.IO.DirectoryNotFoundException">
		/// <paramref name="fileName"/> is an invalid file path.
		/// </exception>
		/// <exception cref="System.IO.FileNotFoundException">
		/// The specified file path could not be found.
		/// </exception>
		/// <exception cref="System.UnauthorizedAccessException">
		/// The user does not have access to write to the specified file.
		/// </exception>
		/// <exception cref="System.IO.IOException">
		/// An I/O error occurred.
		/// </exception>
		public void WriteTo (FormatOptions options, string fileName, CancellationToken cancellationToken = default (CancellationToken))
		{
			if (options == null)
				throw new ArgumentNullException ("options");

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

			using (var stream = File.Open (fileName, FileMode.Create, FileAccess.Write))
				WriteTo (options, stream, cancellationToken);
		}
Esempio n. 15
0
        /// <summary>
        /// Splits the specified message into multiple messages.
        /// </summary>
        /// <remarks>
        /// Splits the specified message into multiple messages, each with a
        /// message/partial body no larger than the max size specified.
        /// </remarks>
        /// <returns>An enumeration of partial messages.</returns>
        /// <param name="message">The message.</param>
        /// <param name="maxSize">The maximum size for each message body.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <paramref name="message"/> is <c>null</c>.
        /// </exception>
        /// <exception cref="System.ArgumentOutOfRangeException">
        /// <paramref name="maxSize"/> is less than <c>1</c>.
        /// </exception>
        public static IEnumerable <MimeMessage> Split(MimeMessage message, int maxSize)
        {
            if (message == null)
            {
                throw new ArgumentNullException(nameof(message));
            }

            if (maxSize < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(maxSize));
            }

            var options = FormatOptions.CloneDefault();

            foreach (HeaderId id in Enum.GetValues(typeof(HeaderId)))
            {
                switch (id)
                {
                case HeaderId.Subject:
                case HeaderId.MessageId:
                case HeaderId.Encrypted:
                case HeaderId.MimeVersion:
                case HeaderId.ContentAlternative:
                case HeaderId.ContentBase:
                case HeaderId.ContentClass:
                case HeaderId.ContentDescription:
                case HeaderId.ContentDisposition:
                case HeaderId.ContentDuration:
                case HeaderId.ContentFeatures:
                case HeaderId.ContentId:
                case HeaderId.ContentIdentifier:
                case HeaderId.ContentLanguage:
                case HeaderId.ContentLength:
                case HeaderId.ContentLocation:
                case HeaderId.ContentMd5:
                case HeaderId.ContentReturn:
                case HeaderId.ContentTransferEncoding:
                case HeaderId.ContentTranslationType:
                case HeaderId.ContentType:
                    break;

                default:
                    options.HiddenHeaders.Add(id);
                    break;
                }
            }

            var memory = new MemoryStream();

            message.WriteTo(options, memory);
            memory.Seek(0, SeekOrigin.Begin);

            if (memory.Length <= maxSize)
            {
                memory.Dispose();

                yield return(message);

                yield break;
            }

            var streams = new List <Stream> ();

#if !NETSTANDARD1_3 && !NETSTANDARD1_6
            var buf = memory.GetBuffer();
#else
            var buf = memory.ToArray();
#endif
            long startIndex = 0;

            while (startIndex < memory.Length)
            {
                // Preferably, we'd split on whole-lines if we can,
                // but if that's not possible, split on max size
                long endIndex = Math.Min(memory.Length, startIndex + maxSize);

                if (endIndex < memory.Length)
                {
                    long ebx = endIndex;

                    while (ebx > (startIndex + 1) && buf[ebx] != (byte)'\n')
                    {
                        ebx--;
                    }

                    if (buf[ebx] == (byte)'\n')
                    {
                        endIndex = ebx + 1;
                    }
                }

                streams.Add(new BoundStream(memory, startIndex, endIndex, true));
                startIndex = endIndex;
            }

            var msgid  = message.MessageId ?? MimeUtils.GenerateMessageId();
            int number = 1;

            foreach (var stream in streams)
            {
                var part = new MessagePartial(msgid, number++, streams.Count)
                {
                    Content = new MimeContent(stream)
                };

                var submessage = CloneMessage(message);
                submessage.MessageId = MimeUtils.GenerateMessageId();
                submessage.Body      = part;

                yield return(submessage);
            }

            yield break;
        }
Esempio n. 16
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);
		}
Esempio n. 17
0
 static FormatOptions()
 {
     Default = new FormatOptions();
 }
Esempio n. 18
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);
			}
		}
Esempio n. 19
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);
                }
            }
        }
Esempio n. 20
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;
		}
Esempio n. 21
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">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 (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(EnsureNewLine));
                    }

                    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);
            }
        }
Esempio n. 22
0
 internal abstract void Encode(FormatOptions options, StringBuilder builder, ref int lineLength);
Esempio n. 23
0
        internal static string FoldPreambleOrEpilogue(FormatOptions options, string text, bool isEpilogue)
        {
            var builder = new StringBuilder();
            int startIndex, wordIndex;
            int lineLength = 0;
            int index      = 0;

            if (isEpilogue)
            {
                builder.Append(options.NewLine);
            }

            while (index < text.Length)
            {
                startIndex = index;

                while (index < text.Length)
                {
                    if (!char.IsWhiteSpace(text[index]))
                    {
                        break;
                    }

                    if (text[index] == '\n')
                    {
                        builder.Append(options.NewLine);
                        startIndex = index + 1;
                        lineLength = 0;
                    }

                    index++;
                }

                wordIndex = index;

                while (index < text.Length && !char.IsWhiteSpace(text[index]))
                {
                    index++;
                }

                int length = index - startIndex;

                if (lineLength > 0 && lineLength + length >= options.MaxLineLength)
                {
                    builder.Append(options.NewLine);
                    length     = index - wordIndex;
                    startIndex = wordIndex;
                    lineLength = 0;
                }

                if (length > 0)
                {
                    builder.Append(text, startIndex, length);
                    lineLength += length;
                }
            }

            if (lineLength > 0)
            {
                builder.Append(options.NewLine);
            }

            return(builder.ToString());
        }
Esempio n. 24
0
        static byte[] EncodeReceivedHeader(ParserOptions options, FormatOptions format, Encoding charset, string field, string value)
        {
            var  tokens     = new List <ReceivedTokenValue> ();
            var  rawValue   = charset.GetBytes(value);
            var  encoded    = new StringBuilder();
            int  lineLength = field.Length + 1;
            bool date       = false;
            int  index      = 0;
            int  count      = 0;

            while (index < rawValue.Length)
            {
                ReceivedTokenValue token = null;
                int startIndex           = index;

                if (!ParseUtils.SkipCommentsAndWhiteSpace(rawValue, ref index, rawValue.Length, false) || index >= rawValue.Length)
                {
                    tokens.Add(new ReceivedTokenValue(startIndex, index - startIndex));
                    break;
                }

                while (index < rawValue.Length && !rawValue[index].IsWhitespace())
                {
                    index++;
                }

                var atom = charset.GetString(rawValue, startIndex, index - startIndex);

                for (int i = 0; i < ReceivedTokens.Length; i++)
                {
                    if (atom == ReceivedTokens[i].Atom)
                    {
                        ReceivedTokens[i].Skip(rawValue, ref index);

                        if (ParseUtils.SkipCommentsAndWhiteSpace(rawValue, ref index, rawValue.Length, false))
                        {
                            if (index < rawValue.Length && rawValue[index] == (byte)';')
                            {
                                date = true;
                                index++;
                            }
                        }

                        token = new ReceivedTokenValue(startIndex, index - startIndex);
                        break;
                    }
                }

                if (token == null)
                {
                    if (ParseUtils.SkipCommentsAndWhiteSpace(rawValue, ref index, rawValue.Length, false))
                    {
                        while (index < rawValue.Length && !rawValue[index].IsWhitespace())
                        {
                            index++;
                        }
                    }

                    token = new ReceivedTokenValue(startIndex, index - startIndex);
                }

                tokens.Add(token);

                ParseUtils.SkipWhiteSpace(rawValue, ref index, rawValue.Length);

                if (date && index < rawValue.Length)
                {
                    // slurp up the date (the final token)
                    tokens.Add(new ReceivedTokenValue(index, rawValue.Length - index));
                    break;
                }
            }

            foreach (var token in tokens)
            {
                var text = charset.GetString(rawValue, token.StartIndex, token.Length).TrimEnd();

                if (count > 0 && lineLength + text.Length + 1 > format.MaxLineLength)
                {
                    encoded.Append(format.NewLine);
                    encoded.Append('\t');
                    lineLength = 1;
                    count      = 0;
                }
                else
                {
                    encoded.Append(' ');
                    lineLength++;
                }

                lineLength += text.Length;
                encoded.Append(text);
                count++;
            }

            encoded.Append(format.NewLine);

            return(charset.GetBytes(encoded.ToString()));
        }
Esempio n. 25
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="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))
        {
            if (Boundary == null)
            {
                Boundary = GenerateBoundary();
            }

            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, cancellationToken);
            }

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

            if (cancellable != null)
            {
                for (int i = 0; i < children.Count; i++)
                {
                    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 (part == null || (part.ContentObject != null && part.ContentObject.Stream.Length != 0))
                    {
                        cancellable.Write(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);
                    }
                }

                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 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 (part == null || (part.ContentObject != null && part.ContentObject.Stream.Length != 0))
                    {
                        stream.Write(options.NewLineBytes, 0, options.NewLineBytes.Length);
                    }
                }

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

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

            if (RawEpilogue != null && RawEpilogue.Length > 0)
            {
                WriteBytes(options, stream, RawEpilogue, cancellationToken);
            }
        }
Esempio n. 26
0
 /// <summary>
 /// Clones an instance of <see cref="MimeKit.FormatOptions"/>.
 /// </summary>
 public FormatOptions Clone()
 {
     var options = new FormatOptions ();
     options.MaxLineLength = MaxLineLength;
     options.NewLineFormat = NewLineFormat;
     options.WriteHeaders = true;
     return options;
 }
Esempio n. 27
0
        EncodeMethod GetEncodeMethod(FormatOptions options, string name, string value, out string quoted)
        {
            var          method = EncodeMethod.None;
            EncodeMethod encode;

            switch (encodingMethod)
            {
            default:
                if (options.ParameterEncodingMethod == ParameterEncodingMethod.Rfc2231)
                {
                    encode = EncodeMethod.Rfc2231;
                }
                else
                {
                    encode = EncodeMethod.Rfc2047;
                }
                break;

            case ParameterEncodingMethod.Rfc2231:
                encode = EncodeMethod.Rfc2231;
                break;

            case ParameterEncodingMethod.Rfc2047:
                encode = EncodeMethod.Rfc2047;
                break;
            }

            quoted = null;

            if (name.Length + 1 + value.Length >= options.MaxLineLength)
            {
                return(encode);
            }

            for (int i = 0; i < value.Length; i++)
            {
                if (value[i] < 128)
                {
                    var c = (byte)value[i];

                    if (c.IsCtrl())
                    {
                        return(encode);
                    }

                    if (!c.IsAttr())
                    {
                        method = EncodeMethod.Quote;
                    }
                }
                else if (options.International)
                {
                    method = EncodeMethod.Quote;
                }
                else
                {
                    return(encode);
                }
            }

            if (method == EncodeMethod.Quote)
            {
                quoted = MimeUtils.Quote(value);

                if (name.Length + 1 + quoted.Length >= options.MaxLineLength)
                {
                    return(encode);
                }
            }

            return(method);
        }
Esempio n. 28
0
		static FormatOptions ()
		{
			Default = new FormatOptions ();
		}
Esempio n. 29
0
        static bool Rfc2231GetNextValue(FormatOptions options, string charset, Encoder encoder, HexEncoder hex, char[] chars, ref int index, ref byte[] bytes, ref byte[] encoded, int maxLength, out string value)
        {
            int length = chars.Length - index;

            if (length < maxLength)
            {
                switch (GetEncodeMethod(options, chars, index, length))
                {
                case EncodeMethod.Quote:
                    value  = MimeUtils.Quote(new string (chars, index, length));
                    index += length;
                    return(false);

                case EncodeMethod.None:
                    value  = new string (chars, index, length);
                    index += length;
                    return(false);
                }
            }

            length = Math.Min(maxLength, length);
            int ratio, count, n;

            do
            {
                count = encoder.GetByteCount(chars, index, length, true);
                if (count > maxLength && length > 1)
                {
                    if ((ratio = (int)Math.Round((double)count / (double)length)) > 1)
                    {
                        length -= Math.Max((count - maxLength) / ratio, 1);
                    }
                    else
                    {
                        length--;
                    }
                    continue;
                }

                if (bytes.Length < count)
                {
                    Array.Resize <byte> (ref bytes, count);
                }

                count = encoder.GetBytes(chars, index, length, bytes, 0, true);

                // Note: the first chunk needs to be encoded in order to declare the charset
                if (index > 0 || charset == "us-ascii")
                {
                    var method = GetEncodeMethod(bytes, count);

                    if (method == EncodeMethod.Quote)
                    {
                        value  = MimeUtils.Quote(Encoding.ASCII.GetString(bytes, 0, count));
                        index += length;
                        return(false);
                    }

                    if (method == EncodeMethod.None)
                    {
                        value  = Encoding.ASCII.GetString(bytes, 0, count);
                        index += length;
                        return(false);
                    }
                }

                n = hex.EstimateOutputLength(count);
                if (encoded.Length < n)
                {
                    Array.Resize <byte> (ref encoded, n);
                }

                // only the first value gets a charset declaration
                int charsetLength = index == 0 ? charset.Length + 2 : 0;

                n = hex.Encode(bytes, 0, count, encoded);
                if (n > 3 && (charsetLength + n) > maxLength)
                {
                    int x = 0;

                    for (int i = n - 1; i >= 0 && charsetLength + i >= maxLength; i--)
                    {
                        if (encoded[i] == (byte)'%')
                        {
                            x--;
                        }
                        else
                        {
                            x++;
                        }
                    }

                    if ((ratio = (int)Math.Round((double)count / (double)length)) > 1)
                    {
                        length -= Math.Max(x / ratio, 1);
                    }
                    else
                    {
                        length--;
                    }
                    continue;
                }

                if (index == 0)
                {
                    value = charset + "''" + Encoding.ASCII.GetString(encoded, 0, n);
                }
                else
                {
                    value = Encoding.ASCII.GetString(encoded, 0, n);
                }
                index += length;
                return(true);
            } while (true);
        }
Esempio n. 30
0
		/// <summary>
		/// Writes the <see cref="MimeKit.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">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 (Message != null)
				Message.WriteTo (options, stream, cancellationToken);
		}
Esempio n. 31
0
        void EncodeRfc2231(FormatOptions options, StringBuilder builder, ref int lineLength, Encoding headerEncoding)
        {
            var    bestEncoding = options.International ? CharsetUtils.UTF8 : GetBestEncoding(Value, encoding ?? headerEncoding);
            int    maxLength = options.MaxLineLength - (Name.Length + 6);
            var    charset = CharsetUtils.GetMimeCharset(bestEncoding);
            var    encoder = (Encoder)bestEncoding.GetEncoder();
            var    bytes = new byte[Math.Max(maxLength, 6)];
            var    hexbuf = new byte[bytes.Length * 3 + 3];
            var    chars = Value.ToCharArray();
            var    hex = new HexEncoder();
            int    index = 0, i = 0;
            string value, id;
            bool   encoded;
            int    length;

            do
            {
                builder.Append(';');
                lineLength++;

                encoded = Rfc2231GetNextValue(options, charset, encoder, hex, chars, ref index, ref bytes, ref hexbuf, maxLength, out value);
                length  = Name.Length + (encoded ? 1 : 0) + 1 + value.Length;

                if (i == 0 && index == chars.Length)
                {
                    if (lineLength + 1 + length >= options.MaxLineLength)
                    {
                        builder.Append(options.NewLine);
                        builder.Append('\t');
                        lineLength = 1;
                    }
                    else
                    {
                        builder.Append(' ');
                        lineLength++;
                    }

                    builder.Append(Name);
                    if (encoded)
                    {
                        builder.Append('*');
                    }
                    builder.Append('=');
                    builder.Append(value);
                    lineLength += length;
                    return;
                }

                builder.Append(options.NewLine);
                builder.Append('\t');
                lineLength = 1;

                id      = i.ToString();
                length += id.Length + 1;

                builder.Append(Name);
                builder.Append('*');
                builder.Append(id);
                if (encoded)
                {
                    builder.Append('*');
                }
                builder.Append('=');
                builder.Append(value);
                lineLength += length;
                i++;
            } while (index < chars.Length);
        }
Esempio n. 32
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);
		}
Esempio n. 33
0
 /// <summary>
 /// Writes the <see cref="MimeKit.MimeEntity"/> to the specified output stream.
 /// </summary>
 /// <remarks>
 /// Writes the entity to the output stream.
 /// </remarks>
 /// <param name="stream">The output stream.</param>
 /// <param name="cancellationToken">A cancellation token.</param>
 /// <exception cref="System.ArgumentNullException">
 /// <paramref name="stream"/> is <c>null</c>.
 /// </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(Stream stream, CancellationToken cancellationToken = default(CancellationToken))
 {
     WriteTo(FormatOptions.GetDefault(), stream, cancellationToken);
 }
Esempio n. 34
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 ();
			}
		}
Esempio n. 35
0
        internal override void Encode(FormatOptions options, StringBuilder builder, ref int lineLength)
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }

            if (lineLength < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(lineLength));
            }

            if (!string.IsNullOrEmpty(Name))
            {
                string name;

                if (!options.International)
                {
                    var encoded = Rfc2047.EncodePhrase(options, Encoding, Name);
                    name = Encoding.ASCII.GetString(encoded, 0, encoded.Length);
                }
                else
                {
                    name = EncodeInternationalizedPhrase(Name);
                }

                if (lineLength + name.Length > options.MaxLineLength)
                {
                    if (name.Length > options.MaxLineLength)
                    {
                        // we need to break up the name...
                        builder.AppendFolded(options, name, ref lineLength);
                    }
                    else
                    {
                        // the name itself is short enough to fit on a single line,
                        // but only if we write it on a line by itself
                        if (lineLength > 1)
                        {
                            builder.LineWrap(options);
                            lineLength = 1;
                        }

                        lineLength += name.Length;
                        builder.Append(name);
                    }
                }
                else
                {
                    // we can safely fit the name on this line...
                    lineLength += name.Length;
                    builder.Append(name);
                }
            }

            builder.Append(": ");
            lineLength += 2;

            Members.Encode(options, builder, ref lineLength);

            builder.Append(';');
            lineLength++;
        }
Esempio n. 36
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);
			}
		}
Esempio n. 37
0
		static byte[] EncodeReferencesHeader (ParserOptions options, FormatOptions format, Encoding charset, string field, string value)
		{
			var encoded = new StringBuilder ();
			int lineLength = field.Length + 1;
			int count = 0;

			foreach (var reference in MimeUtils.EnumerateReferences (value)) {
				if (count > 0 && lineLength + reference.Length + 3 > format.MaxLineLength) {
					encoded.Append (format.NewLine);
					encoded.Append ('\t');
					lineLength = 1;
					count = 0;
				} else {
					encoded.Append (' ');
					lineLength++;
				}

				encoded.Append ('<').Append (reference).Append ('>');
				lineLength += reference.Length + 2;
				count++;
			}

			encoded.Append (format.NewLine);

			return charset.GetBytes (encoded.ToString ());
		}
Esempio n. 38
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);
			}
		}
Esempio n. 39
0
		static IEnumerable<BrokenWord> WordBreak (FormatOptions format, string word, int lineLength)
		{
			var chars = word.ToCharArray ();
			int startIndex = 0;

			lineLength = Math.Max (lineLength, 1);

			while (startIndex < word.Length) {
				int length = Math.Min (format.MaxLineLength - lineLength, word.Length);

				if (char.IsSurrogatePair (word, startIndex + length - 1))
					length--;

				yield return new BrokenWord (chars, startIndex, length);

				startIndex += length;
				lineLength = 1;
			}

			yield break;
		}
Esempio n. 40
0
		ImapCommand QueueAppend (FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset? date, CancellationToken cancellationToken, ITransferProgress progress)
		{
			string format = "APPEND %F";

			if ((flags & SettableFlags) != 0)
				format += " " + ImapUtils.FormatFlagsList (flags, 0);

			if (date.HasValue)
				format += " \"" + ImapUtils.FormatInternalDate (date.Value) + "\"";

			format += " %L\r\n";

			var ic = new ImapCommand (Engine, cancellationToken, null, options, format, this, message);
			ic.Progress = progress;

			Engine.QueueCommand (ic);

			return ic;
		}
Esempio n. 41
0
		internal static string Fold (FormatOptions format, string field, string value)
		{
			var folded = new StringBuilder (value.Length);
			int lineLength = field.Length + 2;
			int lastLwsp = -1;

			folded.Append (' ');

			var words = TokenizeText (value);

			foreach (var word in words) {
				if (IsWhiteSpace (word[0])) {
					if (lineLength + word.Length > format.MaxLineLength) {
						for (int i = 0; i < word.Length; i++) {
							if (lineLength > format.MaxLineLength) {
								folded.Append (format.NewLine);
								lineLength = 0;
							}

							folded.Append (word[i]);
							lineLength++;
						}
					} else {
						lineLength += word.Length;
						folded.Append (word);
					}

					lastLwsp = folded.Length - 1;
					continue;
				}

				if (lastLwsp != -1 && lineLength + word.Length > format.MaxLineLength) {
					folded.Insert (lastLwsp, format.NewLine);
					lineLength = 1;
					lastLwsp = -1;
				}

				if (word.Length > format.MaxLineLength) {
					foreach (var broken in WordBreak (format, word, lineLength)) {
						if (lineLength + broken.Length > format.MaxLineLength) {
							folded.Append (format.NewLine);
							folded.Append (' ');
							lineLength = 1;
						}

						folded.Append (broken.Text, broken.StartIndex, broken.Length);
						lineLength += broken.Length;
					}
				} else {
					lineLength += word.Length;
					folded.Append (word);
				}
			}

			folded.Append (format.NewLine);

			return folded.ToString ();
		}
Esempio n. 42
0
		ImapCommand QueueMultiAppend (FormatOptions options, IList<MimeMessage> messages, IList<MessageFlags> flags, IList<DateTimeOffset> dates, CancellationToken cancellationToken, ITransferProgress progress)
		{
			var args = new List<object> ();
			string format = "APPEND %F";

			args.Add (this);

			for (int i = 0; i < messages.Count; i++) {
				if ((flags[i] & SettableFlags) != 0)
					format += " " + ImapUtils.FormatFlagsList (flags[i], 0);

				if (dates != null)
					format += " \"" + ImapUtils.FormatInternalDate (dates[i]) + "\"";

				format += " %L";

				args.Add (messages[i]);
			}

			format += "\r\n";

			var ic = new ImapCommand (Engine, cancellationToken, null, options, format, args.ToArray ());
			ic.Progress = progress;

			Engine.QueueCommand (ic);

			return ic;
		}
Esempio n. 43
0
		static byte[] EncodeContentDisposition (ParserOptions options, FormatOptions format, Encoding charset, string field, string value)
		{
			var disposition = ContentDisposition.Parse (options, value);
			var encoded = disposition.Encode (format, charset);

			return Encoding.UTF8.GetBytes (encoded);
		}
Esempio n. 44
0
        internal string Encode(FormatOptions options, Encoding charset)
        {
            int lineLength = "Content-Type: ".Length;
            var value = new StringBuilder (" ");

            value.Append (MediaType);
            value.Append ('/');
            value.Append (MediaSubtype);

            Parameters.Encode (options, value, ref lineLength, charset);
            value.Append (options.NewLine);

            return value.ToString ();
        }
Esempio n. 45
0
		static byte[] EncodeContentType (ParserOptions options, FormatOptions format, Encoding charset, string field, string value)
		{
			var contentType = ContentType.Parse (options, value);
			var encoded = contentType.Encode (format, charset);

			return Encoding.UTF8.GetBytes (encoded);
		}
Esempio n. 46
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;
		}
Esempio n. 47
0
		static byte[] EncodeUnstructuredHeader (ParserOptions options, FormatOptions format, Encoding charset, string field, string value)
		{
			if (format.International) {
				var folded = Fold (format, field, value);

				return Encoding.UTF8.GetBytes (folded);
			}

			var encoded = Rfc2047.EncodeText (format, charset, value);

			return Rfc2047.FoldUnstructuredHeader (format, field, encoded);
		}
Esempio n. 48
0
		void AppendString (FormatOptions options, bool allowAtom, MemoryStream builder, string value)
		{
			byte[] buf;

			switch (GetStringType (value, allowAtom)) {
			case ImapStringType.Literal:
				var literal = Encoding.UTF8.GetBytes (value);
				var length = literal.Length.ToString ();
				buf = Encoding.ASCII.GetBytes (length);

				builder.WriteByte ((byte) '{');
				builder.Write (buf, 0, buf.Length);
				if (Engine.IsGMail || (Engine.Capabilities & ImapCapabilities.LiteralPlus) != 0)
					builder.WriteByte ((byte) '+');
				builder.WriteByte ((byte) '}');
				builder.WriteByte ((byte) '\r');
				builder.WriteByte ((byte) '\n');

				if (Engine.IsGMail || (Engine.Capabilities & ImapCapabilities.LiteralPlus) != 0) {
					builder.Write (literal, 0, literal.Length);
				} else {
					parts.Add (new ImapCommandPart (builder.ToArray (), new ImapLiteral (options, literal)));
					builder.SetLength (0);
				}
				break;
			case ImapStringType.QString:
				buf = Encoding.UTF8.GetBytes (MimeUtils.Quote (value));
				builder.Write (buf, 0, buf.Length);
				break;
			case ImapStringType.Atom:
				buf = Encoding.UTF8.GetBytes (value);
				builder.Write (buf, 0, buf.Length);
				break;
			}
		}
Esempio n. 49
0
 static byte[] EncodeMessageIdHeader(ParserOptions options, FormatOptions format, Encoding charset, string field, string value)
 {
     return(charset.GetBytes(" " + value + format.NewLine));
 }