c# - 如何使用 Mailkit/MimeKit IMAP 将所有邮件保存到单个 .mbox 文件?

标签 c# .net email mailkit mimekit

我一直在寻找如何将所有消息保存到单个文件(例如 .mbox 文件)的示例,但没有成功。这是我尝试过但当前无法正常工作的一些代码。

var exportStream = new MemoryStream(); foreach (var uid in uids) { var message = client.Inbox.GetMessage(uid);消息.WriteTo(exportStream);}exportStream.Position = 0;使用 (var fileStream = File.Create(@"C:\temp\results.mbox")) { exportStream.Seek(0, SeekOrigin.Begin);导出Stream.CopyTo(fileStream); }

最佳答案

我在另一个 StackOverflow 问题的评论部分中对您的快速而肮脏的回复有点过于简单化,但基本上您需要做的就是循环 IMAP 文件夹中的消息,然后将它们写入文件流,由 mbox 标记分隔(通常 "From<SPACE><SOMETHING-ELSE><NEW-LINE>" )。

不幸的是,mbox 文件格式并不是特别标准化,因此对于 "From<SPACE>" 之后应该包含哪些内容没有严格的规则可遵循。 。 通常它是帐户的用户名,后跟某种日期格式或其他格式的时间戳,后跟换行序列,但另一个非常常见的标记是 "From -\n" (在 UNIX 上)或 "From -\r\n" (在 Windows 上)。

因此,要将 IMAP 收件箱中的所有邮件保存到单个 mbox 文件中,我们可能会执行以下操作:

using (var mboxStream = File.Create ("Inbox.mbox")) {
    // Create our standard Mbox marker.
    var mboxMarker = Encoding.ASCII.GetBytes ("From -" + Environment.NewLine);

    // Get the full list of message UIDs in the folder
    var uids = client.Inbox.Search (SearchQuery.All);

    // Iterate over each UID, saving each message into the Mbox.
    foreach (var uid in uids) {
        var message = client.Inbox.GetMessage (uid);

        // The start of each message in an Mbox file is marked by a "From "-line.
        mboxStream.Write (mboxMarker, 0, mboxMarker.Length);

        // Since lines beginning with "From " have special meaning to mbox 
        // parsers, we need to somehow make sure that no line of the message
        // begins with "From ". To do that, we create a filtered stream that
        // will munge the From-lines for us.
        using (var filtered = new FilteredStream (mboxStream)) {
            // Add the mbox filter.
            filtered.Add (new MboxFilter ());

            // Write the message to the mbox file, passing through the filter.
            message.WriteTo (filtered);

            // Flush the filtered stream before disposing it.
            filtered.Flush ();
        }
    }
}

MboxFilter 看起来像这样(尚未包含在 MimeKit 中,但可能会在 v3.0 中添加):

//
// MboxFilter.cs
//
// Author: Jeffrey Stedfast <<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="a9c3ccdaddcccdcfc8e9c4c0cadbc6dac6cfdd87cac6c4" rel="noreferrer noopener nofollow">[email protected]</a>>
//
// Copyright (c) 2013-2021 .NET Foundation and Contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

using System;
using System.Collections.Generic;

namespace MimeKit.IO.Filters {
    /// <summary>
    /// A filter that munges lines beginning with "From " by stuffing a '&gt;' into the beginning of the line.
    /// </summary>
    /// <remarks>
    /// <para>Munging Mbox-style "From "-lines is a workaround to prevent Mbox parsers from misinterpreting a
    /// line beginning with "From " as an mbox marker delineating messages. This munging is non-reversable but
    /// is necessary to properly format a message for saving to an Mbox file.</para>
    /// </remarks>
    public class MboxFilter : MimeFilterBase
    {
        const string From = "From ";
        bool midline;

        /// <summary>
        /// Initialize a new instance of the <see cref="MboxFilter"/> class.
        /// </summary>
        /// <remarks>
        /// Creates a new <see cref="MboxFilter"/>.
        /// </remarks>
        public MboxFilter ()
        {
        }

        static bool StartsWithFrom (byte[] input, int startIndex, int endIndex)
        {
            for (int i = 0, index = startIndex; i < From.Length && index < endIndex; i++, index++) {
                if (input[index] != (byte) From[i])
                    return false;
            }

            return true;
        }

        /// <summary>
        /// Filter the specified input.
        /// </summary>
        /// <remarks>
        /// Filters the specified input buffer starting at the given index,
        /// spanning across the specified number of bytes.
        /// </remarks>
        /// <returns>The filtered output.</returns>
        /// <param name="input">The input buffer.</param>
        /// <param name="startIndex">The starting index of the input buffer.</param>
        /// <param name="length">The length of the input buffer, starting at <paramref name="startIndex"/>.</param>
        /// <param name="outputIndex">The output index.</param>
        /// <param name="outputLength">The output length.</param>
        /// <param name="flush">If set to <c>true</c>, all internally buffered data should be flushed to the output buffer.</param>
        protected override byte[] Filter (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength, bool flush)
        {
            var fromOffsets = new List<int> ();
            int endIndex = startIndex + length;
            int index = startIndex;
            int left;

            while (index < endIndex) {
                byte c = 0;

                if (midline) {
                    while (index < endIndex) {
                        c = input[index++];
                        if (c == (byte) '\n')
                            break;
                    }
                }

                if (c == (byte) '\n' || !midline) {
                    if ((left = endIndex - index) > 0) {
                        midline = true;

                        if (left < 5) {
                            if (StartsWithFrom (input, index, endIndex)) {
                                SaveRemainingInput (input, index, left);
                                endIndex = index;
                                midline = false;
                                break;
                            }
                        } else {
                            if (StartsWithFrom (input, index, endIndex)) {
                                fromOffsets.Add (index);
                                index += 5;
                            }
                        }
                    } else {
                        midline = false;
                    }
                }
            }

            if (fromOffsets.Count > 0) {
                int need = (endIndex - startIndex) + fromOffsets.Count;

                EnsureOutputSize (need, false);
                outputLength = 0;
                outputIndex = 0;

                index = startIndex;
                foreach (var offset in fromOffsets) {
                    if (index < offset) {
                        Buffer.BlockCopy (input, index, OutputBuffer, outputLength, offset - index);
                        outputLength += offset - index;
                        index = offset;
                    }

                    // munge the beginning of the "From "-line.
                    OutputBuffer[outputLength++] = (byte) '>';
                }

                Buffer.BlockCopy (input, index, OutputBuffer, outputLength, endIndex - index);
                outputLength += endIndex - index;

                return OutputBuffer;
            }

            outputLength = endIndex - startIndex;
            outputIndex = 0;
            return input;
        }

        /// <summary>
        /// Resets the filter.
        /// </summary>
        /// <remarks>
        /// Resets the filter.
        /// </remarks>
        public override void Reset ()
        {
            midline = false;
            base.Reset ();
        }
    }
}

关于c# - 如何使用 Mailkit/MimeKit IMAP 将所有邮件保存到单个 .mbox 文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69260543/

相关文章:

.net - 有了这个技术,是否可以在Flash中编译运行silverlight IL?

c# - 从并行任务问题更新图像文件

python - 使用不同文本集的参数网格搜索,用于字典创建和交叉验证

c# - 您如何确定 Internet 连接是否可用于您的 WinForms 应用程序?

.net - 使用事件目录中的信息解锁窗口

asp.net - 48 小时后在 asp.net 中过期确认电子邮件

c# - 部署到主机后,使用 Gmail 通过 C# 发送邮件不起作用

c# - Visual C# - 字符串追加到字符串无法正常工作

c# - 从函数返回日期 int 值

c# - 如何在 asp.net core 中更改根路径 ~/in Razor