如何使用 Mailkit / MimeKit IMAP 将所有消息保存到单个 .mbox 文件?

2024-04-26

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

var exportStream = new MemoryStream(); foreach (var uid in uids) { var message = client.Inbox.GetMessage(uid); message.WriteTo(exportStream);} exportStream.Position = 0; using (var fileStream = File.Create(@"C:\temp\results.mbox")) { exportStream.Seek(0, SeekOrigin.Begin); exportStream.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 <[email protected] /cdn-cgi/l/email-protection>
//
// 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 ();
        }
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 Mailkit / MimeKit IMAP 将所有消息保存到单个 .mbox 文件? 的相关文章

  • CRTP 能否完全取代小型设计的虚拟功能?

    Is CRTP http en wikipedia org wiki Curiously recurring template pattern有足够的能力智胜virtual功能齐全 我认为 CRTP 的唯一缺点是为每个重复模式生成大量代码
  • 使用工作表作为数据源的 VSTO Excel 的简单示例

    我想我遇到了 最简单的答案是最难找到的答案 的情况 而且我还没有遇到过任何搜索能够以直接的方式给我这个答案 这是为了Excel 2010 and VS 2010在现有 VSTO C 项目中 我有一个 Excel 工作表 其中包含 4 列数据
  • 如何从 UNC 中提取服务器名称

    谁能告诉我如何从 UNC 中提取服务器名称 ex 服务器名称 目录 目录 编辑 我很抱歉 但看起来我需要澄清一个错误 路径实际上更像是 服务器名 d 目录 我知道这可能会改变一些事情 怎么样Uri Uri uri new Uri serve
  • 如何测试 PARTIAL 视图在 C# ASP .NET MVC 中呈现

    我有一个视图 它内部有部分视图渲染 div class partialViewDiv Html RenderPartial partial Model SomeModelProperty div 和一个返回此视图的控制器 public Ac
  • 一个阻塞但非模态的 QDialog?

    我有一堆图像 我想对其执行一些操作 处理完每个图像后 我的程序应该弹出一个对话框 提示用户是否要继续处理下一个图像或中止 在此之前 他们应该有机会对图像或参数进行一些手动更改 无论如何 他们必须能够访问应用程序的窗口 而调用对话框的方法的执
  • 使用正在运行的进程的共享内存收集核心转储

    核心转储仅收集进程空间 而不收集为进程间通信创建的共享内存 如何使核心转储也包含正在运行的进程的共享内存 设置核心文件过滤器 proc PID coredump filter per http man7 org linux man page
  • 恢复多个监视器的窗口大小/位置

    许多帖子都涉及恢复 WinForm 位置和大小 例子 www stackoverflow com questions 92540 save and restore form position and size http www stacko
  • 我应该在查询时调用 ToListAsync()

    不久前 我开始接触 C 并正在寻找一些如何编写代码的最佳实践 现在 我正在使用 EF Core 并具有以下代码 var details dbContext Details Where x gt x Name Button foreach v
  • 您在 C# 或 .NET 中见过的最奇怪的极端情况是什么? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • gcc总是做这种优化吗? (公共子表达式消除)

    作为示例 假设表达式sys gt pot atoms item gt P kind mass在循环内求值 循环只改变item 因此表达式可以简化为atoms item gt P kind mass通过将变量定义为atoms sys gt p
  • 列表框显示类名称而不是值

    我正在开发一个项目 其中用户应该向动物输入值 名称 年龄 性别等 并且用户输入的值应该显示在列表框中 这些类相互继承 以下是继承的工作原理 Animalclass 是所有类的父类 Mammal类继承自Animal class Dog类继承自
  • 创建 .ICS 文件,添加到 Outlook

    我正在创建一个简单的应用程序 允许用户下载 ICS 文件 并将其导入到他们选择的日历应用程序 站点中 我对创建过程感到满意 但对在 Outlook 中打开它们有疑问 将使用C ASP NET进行开发 当我打开一个日历时 它会添加一个新日历
  • 从 WMI 运行 exe 时的网络身份验证

    我有一个 C exe 需要使用 WMI 运行并访问网络共享 但是 当我访问共享时 我收到 UnauthorizedAccessException 如果我直接运行 exe 则可以访问共享 我在这两种情况下都使用相同的用户帐户 我的应用程序有两
  • 串行端口轮询和数据处理

    我正在尝试通过微控制器从传感器的多个串行端口读取数据 每个串口将接收超过2000个测量值 每个测量值7个字节 全部为十六进制 而且他们同时开火 现在我正在从 4 个串行端口进行轮询 另外 我将每个测量值转换为字符串并将其附加到字符串构建器
  • C# - 使用 Linq 获取 Attribute 的属性

    我有一个属性 它本身就有属性 我想访问这些属性之一 布尔值 并检查它是否正确 我能够检查属性是否已设置 但这就是全部 至少对于 linq 来说是这样 属性 public class ImportParameter System Attrib
  • 如何修复 Delphi Prism ASP.NET 错误:“解析器错误消息:‘Oxygene’不是受支持的语言”

    我在 Delphi Prism 中编写了一个 ASP NET Web 应用程序 不是网站 在我的开发机器上一切正常 但是当我将其安装在测试服务器上时 出现以下错误 Server Error in MyApp Application Pars
  • 使用 std::set 时重载运算符<

    这是我第一次使用 std set 容器 并且我对操作符 std less 遇到了问题 我声明该集合 std set
  • C++ 中的 Ofstream 数组

    我想要在我的项目中使用 41 个输出文件来在其上写入文本 首先创建一个字符串数组list为了命名这些输出文件 然后我尝试定义一个 ofstream 对象数组并使用list命名它们 但我收到此错误 outfile cannot be used
  • 如果 foreach 是一个结构数组,它会复制每个元素吗?

    我有一个结构数组 做foreach运算符在迭代数组时复制每个元素 据我所理解foreach只是底层的语法糖转换为for 所以看来答案是否定的 但我很想得到一些确认 PS 看来应该有人已经问过了 但我无法轻易找到任何东西 因此 请以提供的参考
  • 如何注销多个非当前用户的会员用户?

    我正在使用属于 MVC2 默认项目一部分的 MembershipProvider 我希望能够获取用户名列表 注销用户 并在需要时销毁他们的会话 我能想到的最接近的是 foreach string userName in UserNames

随机推荐