7// ThreadSubject returns the base subject to use for matching against other
8// messages, to see if they belong to the same thread. A matching subject is
9// always required to match to an existing thread, both if
10// References/In-Reply-To header(s) are present, and if not.
12// Subject should already be q/b-word-decoded.
14// If allowNull is true, base subjects with a \0 can be returned. If not set,
15// an empty string is returned if a base subject would have a \0.
16func ThreadSubject(subject string, allowNull bool) (threadSubject string, isResponse bool) {
17 subject = strings.ToLower(subject)
21 for _, c := range subject {
24 } else if c == ' ' || c == '\n' || c == '\t' {
25 if !strings.HasSuffix(s, " ") {
34 removeBlob := func(s string) string {
43 s = s[i+1:] // Past [...].
44 s = strings.TrimRight(s, " \t") // *WSP
51 removeLeader := func(s string) string {
52 if strings.HasPrefix(s, " ") || strings.HasPrefix(s, "\t") {
58 // Remove zero or more subj-blob
67 if strings.HasPrefix(s, "re") {
69 } else if strings.HasPrefix(s, "fwd") {
71 } else if strings.HasPrefix(s, "fw") {
76 s = strings.TrimLeft(s, " \t") // *WSP
78 if !strings.HasPrefix(s, ":") {
90 s = strings.TrimRight(s, " \t")
91 if strings.HasSuffix(s, "(fwd)") {
92 s = strings.TrimSuffix(s, "(fwd)")
102 s = removeLeader(s) // Step 3.
103 if ns := removeBlob(s); ns != "" {
113 if strings.HasPrefix(s, "[fwd:") && strings.HasSuffix(s, "]") {
114 s = s[len("[fwd:") : len(s)-1]
116 continue // From step 2 again.
120 if !allowNull && strings.ContainsRune(s, 0) {