-
Notifications
You must be signed in to change notification settings - Fork 122
Add support for RFC2369 #171
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,7 @@ import ( | |
| "errors" | ||
| "fmt" | ||
| "net/mail" | ||
| "net/url" | ||
| "os" | ||
| "strconv" | ||
| "strings" | ||
|
|
@@ -211,6 +212,61 @@ func (p *headerParser) parseMsgID() (string, error) { | |
| return left + "@" + right, nil | ||
| } | ||
|
|
||
| func (p *headerParser) parseListCommand() (*url.URL, error) { | ||
| if !p.skipCFWS() { | ||
| return nil, errors.New("mail: malformed parenthetical comment") | ||
| } | ||
|
|
||
| // Consume a potential newline + indent. | ||
| p.consume('\r') | ||
| p.consume('\n') | ||
| p.skipSpace() | ||
|
|
||
| if p.consume('N') && p.consume('O') { | ||
| if !p.skipCFWS() { | ||
| return nil, errors.New("mail: malformed parenthetical comment") | ||
| } | ||
|
|
||
| return nil, nil | ||
| } | ||
|
|
||
| if !p.consume('<') { | ||
| return nil, errors.New("mail: missing '<' in list command") | ||
| } | ||
|
|
||
| i := 0 | ||
| for p.s[i] != '>' && i+1 < len(p.s) { | ||
| i += 1 | ||
| } | ||
|
Comment on lines
+237
to
+240
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably can be simplified with |
||
|
|
||
| var lit string | ||
| lit, p.s = p.s[:i], p.s[i:] | ||
|
|
||
| u, err := url.Parse(lit) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The RFC recommends that we remove any whitespace character from the string in-between the angle brackets. |
||
| if err != nil { | ||
| return u, errors.New("mail: malformed URL") | ||
| } | ||
|
|
||
| if !p.consume('>') { | ||
| return nil, errors.New("mail: missing '>' in list command") | ||
| } | ||
|
|
||
| if !p.skipCFWS() { | ||
| return nil, errors.New("mail: malformed parenthetical comment") | ||
| } | ||
|
|
||
| // If there isn't a comma, we don't care because it means that there aren't | ||
| // any other list command URLs. | ||
| p.consume(',') | ||
| p.skipSpace() | ||
|
|
||
| // Consume a potential newline. | ||
| p.consume('\r') | ||
| p.consume('\n') | ||
|
|
||
| return u, nil | ||
| } | ||
|
|
||
| // A Header is a mail header. | ||
| type Header struct { | ||
| message.Header | ||
|
|
@@ -308,6 +364,35 @@ func (h *Header) MsgIDList(key string) ([]string, error) { | |
| return l, nil | ||
| } | ||
|
|
||
| // MsgIDList parses a list of URLs from a list command header. It returns URLs. | ||
| // If the header field is missing, it returns nil. | ||
| // | ||
| // This can be used on List-Help, List-Unsubscribe, List-Subscribe, List-Post, | ||
| // List-Owner, and List-Archive headers. | ||
| // | ||
| // See https://www.rfc-editor.org/rfc/rfc2369 for more information. | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: we can just use plaintext to reference the RFC: "See RFC 2369". GoDoc will automatically linkify it, and it's more readable. |
||
| // | ||
| // In the case that the value of List-Post is the special value, "NO", the | ||
| // return value is a slice containing one element, nil. | ||
| func (h *Header) ListCommandURLList(key string) ([]*url.URL, error) { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we should just use the name |
||
| v := h.Get(key) | ||
| if v == "" { | ||
| return nil, nil | ||
| } | ||
|
|
||
| p := headerParser{v} | ||
| var l []*url.URL | ||
| for !p.empty() { | ||
| url, err := p.parseListCommand() | ||
| if err != nil { | ||
| return l, err | ||
| } | ||
| l = append(l, url) | ||
| } | ||
|
|
||
| return l, nil | ||
| } | ||
|
|
||
| // GenerateMessageID wraps GenerateMessageIDWithHostname and therefore uses the | ||
| // hostname of the local machine. This is done to not break existing software. | ||
| // Wherever possible better use GenerateMessageIDWithHostname, because the local | ||
|
|
@@ -362,6 +447,18 @@ func (h *Header) SetMsgIDList(key string, l []string) { | |
| } | ||
| } | ||
|
|
||
| func (h *Header) SetListCommandURLList(key string, urls []*url.URL) { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be nice to document this method. |
||
| if len(urls) == 0 { | ||
| h.Del(key) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need to return here, or else we will later on set the header to |
||
| } | ||
|
|
||
| var ids []string | ||
| for _, url := range urls { | ||
| ids = append(ids, url.String()) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we could handle |
||
| } | ||
| h.Set(key, "<"+strings.Join(ids, ">, <")+">") | ||
| } | ||
|
|
||
| // Copy creates a stand-alone copy of the header. | ||
| func (h *Header) Copy() Header { | ||
| return Header{h.Header.Copy()} | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the input values ever contain newlines. These are stripped by the header parser when unfolding the header fields.