使用本地消息从Thunderbird通过Delphi复制电子邮件时遇到编码问题



我正在thunderbird中编写一个插件,使用本地消息传递(遵循python中的乒乓示例(来调用Delphi程序,以将电子邮件复制为"。eml";文件我面临的问题似乎是编码问题。此外,生成的文件在文件的开头和结尾都包含双引号("(以及转义的双引号(\"(。我只想有一个1比1的副本,而不是改变它的内容。

邮件内容示例:

"test"
€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ
éöàäèüâêû 

然而,在文件中,它看起来更像这样:

"test"
€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“â€â€¢â€“—˜™š›œžŸ
éöà äèüâêû

我可能已经发现了问题,这里有解释:

https://www.i18nqa.com/debug/utf8-debug.html

然而,我真的不知道如何调整我的代码来解决这个问题。

谢谢你的帮助!

这是我的背景.js:

async function main() {
messenger.menus.create({
contexts : ["message_list"],
id: "copy@mail.lu",
onclick : passMsg,
title: messenger.i18n.getMessage("lang.menuTitle")
});
}

async function passMsg(OnClickData) {
if (OnClickData.selectedMessages && OnClickData.selectedMessages.messages.length > 0) {
let MessageHeader = OnClickData.selectedMessages.messages[0];
let raw = await messenger.messages.getRaw(MessageHeader.id);
let port=browser.runtime.connectNative("copymail");

port.onMessage.addListener((message) => {
port.disconnect();
});

port.postMessage(raw);
} else {
console.log("No message selected");
}
}
main();

这是我的Delphi代码:

procedure WriteSTDInputToFile(const Filename: String);
var
Buffer:    array [0 .. 3] of Byte;
msgLen:    LongInt;
msg:       UTF8String;
myFile:    TextFile;
StdIn:     THandleStream;
jsonValue: TJSONValue;
begin
StdIn  := THandleStream.Create(GetStdHandle(STD_INPUT_HANDLE));

try
msgLen    := 0;
if StdIn.Read(Buffer, SizeOf(msgLen)) > 0 then
msgLen := PLongInt(@Buffer)^;

if msgLen > 0 then
begin
SetLength(msg, msgLen);
StdIn.Read(PUTF8Char(msg)^, msgLen);

if msg <> '' then
begin
AssignFile(myFile, Filename, CP_UTF8);
ReWrite(myFile);

jsonValue := TJSONObject.ParseJSONValue(msg);

try
write(myFile, UTF8Encode(jsonValue.ToString));
finally
jsonValue.Free;
end;

CloseFile(myFile);

end;
end;

finally
if Assigned(StdIn) then
StdIn.Free;
end;

end;

结果文件内容:

"X-MDAV-Result: clean
X-MDAV-Processed: mail.test.lu, Wed, 28 Oct 2020 08:13:22 +0100
X-Spam-Processed: mail.test.lu, Wed, 28 Oct 2020 08:13:22 +0100
Return-path: <copy@mail.lu>
X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on MAIL01E
X-Spam-Level: 
X-Spam-Status: No, score=0.7 required=10.0 tests=HTML_MESSAGE,MPART_ALT_DIFF
shortcircuit=no autolearn=disabled version=3.4.2
Authentication-Results: test.lu;
auth=pass (plain) smtp.auth=ascholtes@test.lu
Received: from [172.16.17.35] [(172.16.17.35)] by test.lu (172.31.3.6) with ESMTPSA id md50033234892.msg; 
Wed, 28 Oct 2020 08:13:21 +0100
X-MDRemoteIP: 172.16.17.35
X-MDArrival-Date: Wed, 28 Oct 2020 08:13:21 +0100
X-Authenticated-Sender: ascholtes@test.lu
X-Rcpt-To: copy@mail.lu
X-MDRcpt-To: copy@mail.lu
X-Return-Path: copy@mail.lu
X-Envelope-From: copy@mail.lu
X-MDaemon-Deliver-To: ascholtes@test.lu
To: Ayuth Scholtes <copy@mail.lu>
From: Ayuth Scholtes <copy@mail.lu>
Subject: Test
Organization: CISS
Message-ID: <7eb36f7c-a7af-c272-c189-eded642c3e1c@test.lu>
Date: Wed, 28 Oct 2020 08:13:21 +0100
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101
Thunderbird/68.10.0
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="------------6068A746223BB2C9F1771938"
Content-Language: lb-LU
This is a multi-part message in MIME format.
--------------6068A746223BB2C9F1771938
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Transfer-Encoding: 8bit
|"test" â¬âÆââ¦â â¡Ëâ°Å â¹ÅŽâââââ¢ââËâ¢Å¡âºÅžŸ éöàäèüâêû|

--------------6068A746223BB2C9F1771938
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: 8bit
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
<pre class="lang-pascal s-code-block hljs delphi"><code>"test"
â¬âÆââ¦â â¡Ëâ°Å â¹ÅŽâââââ¢ââËâ¢Å¡âºÅžŸ
éöàäèüâêû</code></pre>
</body>
</html>
--------------6068A746223BB2C9F1771938--
"

首先让我说,您在使用本地消息传递在web扩展(Thunderbird插件(和本地应用程序之间传输数据方面做得很好。理解和设置它并不容易,但你在问题中描述了一些小故障,成功地传输了所需的数据。

。。。生成的文件在文件的开头和结尾包含双引号("(以及转义的双引号(CCD _2(

在加载项中,您可以获得原始电子邮件数据作为字符串-console.log(typeof raw)给出string,然后将其传递给port.postMessage。尽管文档中说它需要表示消息的JSON对象来发送,但它似乎接受单个字符串值,根据某些标准,这是有效的JSON。在Delphi代码中,您通过STDIN接收消息,并使用TJSONObject.ParseJSONValue将其解析为TJSONValue。它实际上将创建TJSONString的实例。您可以通过检查jsonValue.ClassName的值来验证这一点。当您使用jsonValue.ToString时,引号的问题就会出现,CCD_11返回的字符串的引号版本与解析前基本相同。使用Value属性返回原始字符串值。

单独使用jsonValue.Value对解决编码问题没有帮助。从电子邮件客户端获得的原始邮件数据采用EML格式。它符合RFC-822,这意味着它是ASCII编码的,但它可以包含任意编码的消息部分(请参阅您自己的示例EML(。由于您只想将EML文件保存为不考虑任何编码,因此最好是传输EML的原始字节,但这并不是Javascript和本机消息API支持的最佳方式。因此,我建议您将Base64编码的数据字符串发送到本机应用程序,在那里您可以将其解码为可以直接写入磁盘的原始字节。

要将原始消息数据编码为附加组件中的Base64字符串,请使用函数btoa:

port.postMessage(btoa(raw));

要在本机应用程序中接收消息,您可以执行以下操作:

uses
System.SysUtils, System.Classes, System.IOUtils, System.JSON, System.NetEncoding, Winapi.Windows;
procedure WriteSTDInputToFile(const FileName: string);
var
StdIn: THandleStream;
MsgLen: Cardinal;
Data: TBytes;
JSONValue: TJSONValue;
begin
StdIn := THandleStream.Create(GetStdHandle(STD_INPUT_HANDLE));
try
StdIn.ReadBuffer(MsgLen, SizeOf(MsgLen));
SetLength(Data, MsgLen);
StdIn.ReadBuffer(Data, MsgLen);
JSONValue := TJSONObject.ParseJSONValue(Data, 0);
Data := TNetEncoding.Base64.DecodeStringToBytes(JSONValue.Value);
TFile.WriteAllBytes(FileName, Data);
finally
StdIn.Free;
end;
end;

注意原始代码的几个改进:

  • Cardinal类型用于MsgLen。该协议定义,输入端的前4个字节表示以32位无符号整数表示的字节的消息长度。Cardinal是Delphi的本机类型,也可以使用UInt32别名
  • 我使用了ReadBuffer方法而不是Read来读取STDIN,这使得程序在出现一些意外情况时崩溃。理想情况下,您应该处理这种情况,通过STDOUT发送错误消息作为响应,并在插件中处理响应
  • 我不将传统的I/O例程与流混合。我甚至并没有使用流来编写代码中的输出文件。由于"1中的File.WriteAllBytes,创建文件只是一行代码
  • 我不检查if Assigned(StdIn) then StdIn.Free;。这就是Free已经为您所做的

知道传入消息是一个引用Base64编码的字符串,就可以省略JSON处理,这样代码就变成:

procedure WriteSTDInputToFile(const FileName: string);
var
StdIn: THandleStream;
MsgLen: Cardinal;
Msg: RawByteString;
Data: TBytes;
begin
StdIn := THandleStream.Create(GetStdHandle(STD_INPUT_HANDLE));
try
StdIn.ReadBuffer(MsgLen, SizeOf(MsgLen));
StdIn.Seek(1, soFromCurrent); { skip double quote }
SetLength(Msg, MsgLen - 2); { minus leading and trailing double quotes }
StdIn.ReadBuffer(Msg[Low(Msg)], MsgLen);
Data := TNetEncoding.Base64.DecodeStringToBytes(UTF8ToString(Msg));
TFile.WriteAllBytes(FileName, Data);
finally
StdIn.Free;
end;
end;

最新更新