Go(语言)解析电子邮件头并保持顺序。

huangapple go评论94阅读模式
英文:

Go (lang) parsing an email header and keeping order

问题

我正在使用Go语言的net/mail库,一切都很好,但是我想传入一个原始的电子邮件并保持头部的顺序。这很重要,因为传递消息的邮件服务器会按顺序添加它们的头部。如果没有顺序,很难知道谁在何时接收了什么以及每个服务器添加了哪些头部。

net/mail库将头部存储在一个映射中,根据定义,映射没有顺序的概念。这似乎是一个奇怪的选择,因为头部的顺序仅基于电子邮件中的顺序,但事实确实如此。

有人有任何关于如何保留头部读取顺序的建议吗?

谢谢

英文:

I'm using net/mail library in Go, everything is great, however I want to pass in an original email and keep the order of the headers. This is important because the mail servers that pass the message on each add their headers in an order. Without order, its hard to know who received what, when and what headers each server added.
The net/mail library stores the headers in a map, which by definition has no concept of order. Seems a strange choice as header order is based only on order in the email, but it is the case.
Anyone got any suggestions as to how I can retain order the headers were read?

Thanks

答案1

得分: 1

net/mail包使用net/textproto包来解析邮件头(参见ReadMessage())。具体来说,它使用[ReadMIMEHeader()][2]来解析头部,其文档如下:

> 返回的映射m将CanonicalMIMEHeaderKey(key)映射到与输入中遇到的相同顺序的值序列。

如果你想要的话,可以[查看完整的源代码][3],但基本的过程如下:

headers = make(map[string][]string)
for {
	key, value := readNextHeader()
	if key == "" {
		return headers // 头部结束
	}

	if headers[key] == nil {
		headers[key] = []string{value}
	} else {
		headers[key] = append(headers[key], value)
	}
}

确实,邮件中头部的原始顺序会丢失,但我不知道有任何真正需要保留这个顺序的情况。丢失的是多值头部的顺序。切片确保它们与电子邮件中出现的顺序相同。

你可以使用一个简单的程序循环遍历头部并比较值来验证这一点(例如Playground中的这个程序)。


然而,匹配ReceivedReceived-SPF头部要复杂一些,因为:

  1. 并非每个Received头部都有对应的Received-SPF头部;
  2. Received-SPF头部可能不会出现在Received头部之上;这是[RFC推荐但并非强制要求的][4](此外,许多程序甚至不遵循RFC,所以这也不能保证)。

因此,你需要解析头部的值并基于此进行匹配,或者使用net/textproto包来更低级地访问头部。你可以使用[ReadMIMEHeader()][3]的源代码作为起点。

1: https://github.com/golang/go/blob/master/src/net/mail/message.go#L53)
[2]: https://golang.org/pkg/net/textproto/#Reader.ReadMIMEHeader
[3]: https://github.com/golang/go/blob/master/src/net/textproto/reader.go#L468
[4]: http://www.openspf.org/RFC_4408#header-field

英文:

The net/mail package uses the net/textproto package to parse the headers
(see ReadMessage()). Specifically, it uses [ReadMIMEHeader()][2] for
the headers, which is documented as:

> The returned map m maps CanonicalMIMEHeaderKey(key) to a sequence of values
> in the same order encountered in the input.

You can [view the full source][3] if you want, but the basic process is:

headers = make(map[string][]string)
for {
	key, value := readNextHeader()
	if key == "" {
		return headers // End of headers
	}

	if headers[key] == nil {
		headers[key] = []string{value}
	} else {
		headers[key] = append(headers[key], value)
	}
}

It's true that the original order of the headers as they appeared in the message
is lost, but I'm not aware of any scenario where this truly matters. What
isn't lost is the order of the multi-values headers. The slice ensures they're
in the same order as they appeared in the email.

You can verify this with a simple program which loops over the headers and
compares the values (such as this one in the
Playground
).


However, matching Received and Received-SPF headers is a bit more complex,
as:

  1. not every Received header may have a corresponding Received-SPF header;
  2. the Received-SPF header may not appear above the Received header; this is
    [recommended but not mandated by the RFC][4] (besides, many programs don't
    even follow the RFC, so this wouldn't be a guarantee anyway).

So you'll either need to parse the value of the headers and match them based on
that, or use the net/textproto package for more low-level access to the
headers. You can use the source of [ReadMIMEHeader()][3] as a starting point.

1: https://github.com/golang/go/blob/master/src/net/mail/message.go#L53)
[2]: https://golang.org/pkg/net/textproto/#Reader.ReadMIMEHeader
[3]: https://github.com/golang/go/blob/master/src/net/textproto/reader.go#L468
[4]: http://www.openspf.org/RFC_4408#header-field

huangapple
  • 本文由 发表于 2017年5月5日 22:44:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/43807868.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定