Merge pull request #169 from cathugger/master
srnd: custom email address formatter, some tweaks
This commit is contained in:
commit
a1d11c594c
@ -19,7 +19,6 @@ import (
|
||||
"log"
|
||||
"mime"
|
||||
"net/http"
|
||||
"net/mail"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@ -785,10 +784,10 @@ func (self *httpFrontend) handle_postRequest(pr *postRequest, b bannedFunc, e er
|
||||
return
|
||||
}
|
||||
|
||||
subject := pr.Subject
|
||||
subject := strings.TrimSpace(pr.Subject)
|
||||
|
||||
// set subject
|
||||
if len(subject) == 0 {
|
||||
if subject == "" {
|
||||
subject = "None"
|
||||
} else if len(subject) > 256 {
|
||||
// subject too big
|
||||
@ -796,29 +795,21 @@ func (self *httpFrontend) handle_postRequest(pr *postRequest, b bannedFunc, e er
|
||||
return
|
||||
}
|
||||
|
||||
nntp.headers.Set("Subject", subject)
|
||||
nntp.headers.Set("Subject", safeHeader(subject))
|
||||
if isSage(subject) {
|
||||
nntp.headers.Set("X-Sage", "1")
|
||||
}
|
||||
|
||||
name := pr.Name
|
||||
|
||||
name := strings.TrimSpace(pr.Name)
|
||||
var tripcode_privkey []byte
|
||||
|
||||
// set name
|
||||
if len(name) == 0 {
|
||||
name = "Anonymous"
|
||||
} else {
|
||||
idx := strings.Index(name, "#")
|
||||
// tripcode
|
||||
if idx >= 0 {
|
||||
if idx := strings.IndexByte(name, '#'); idx >= 0 {
|
||||
tripcode_privkey = parseTripcodeSecret(name[idx+1:])
|
||||
name = strings.Trim(name[:idx], "\t ")
|
||||
name = strings.TrimSpace(name[:idx])
|
||||
}
|
||||
if name == "" {
|
||||
name = "Anonymous"
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(name) > 128 {
|
||||
// name too long
|
||||
e(errors.New("name too long"))
|
||||
@ -830,10 +821,7 @@ func (self *httpFrontend) handle_postRequest(pr *postRequest, b bannedFunc, e er
|
||||
msgid = genMessageID(pr.Frontend)
|
||||
}
|
||||
|
||||
nntp.headers.Set("From", (&mail.Address{
|
||||
Name: name,
|
||||
Address: "poster@" + pr.Frontend,
|
||||
}).String())
|
||||
nntp.headers.Set("From", formatAddress(safeHeader(name), "poster@" + pr.Frontend))
|
||||
nntp.headers.Set("Message-ID", msgid)
|
||||
|
||||
// set message
|
||||
|
@ -156,10 +156,7 @@ func newPlaintextArticle(message, email, subject, name, instance, message_id, ne
|
||||
nntp := &nntpArticle{
|
||||
headers: make(ArticleHeaders),
|
||||
}
|
||||
nntp.headers.Set("From", (&mail.Address{
|
||||
Name: name,
|
||||
Address: email,
|
||||
}).String())
|
||||
nntp.headers.Set("From", formatAddress(name, email))
|
||||
nntp.headers.Set("Subject", subject)
|
||||
if isSage(subject) {
|
||||
nntp.headers.Set("X-Sage", "1")
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"io"
|
||||
"log"
|
||||
"mime"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/mail"
|
||||
@ -27,6 +28,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func DelFile(fname string) {
|
||||
@ -174,6 +176,121 @@ func safeHeader(s string) string {
|
||||
return strings.TrimSpace(safeHeaderReplacer.Replace(s))
|
||||
}
|
||||
|
||||
func isVchar(r rune) bool {
|
||||
// RFC 5234 B.1: VCHAR = %x21-7E ; visible (printing) characters
|
||||
// RFC 6532 3.2: VCHAR =/ UTF8-non-ascii
|
||||
return (r >= 0x21 && r <= 0x7E) || r >= 0x80
|
||||
}
|
||||
|
||||
func isAtext(r rune) bool {
|
||||
// RFC 5322: Printable US-ASCII characters not including specials. Used for atoms.
|
||||
switch r {
|
||||
case '(', ')', '<', '>', '[', ']', ':', ';', '@', '\\', ',', '.', '"':
|
||||
return false
|
||||
}
|
||||
return isVchar(r)
|
||||
}
|
||||
|
||||
func isWSP(r rune) bool { return r == ' ' || r == '\t' }
|
||||
|
||||
func isQtext(r rune) bool {
|
||||
if r == '\\' || r == '"' {
|
||||
return false
|
||||
}
|
||||
return isVchar(r)
|
||||
}
|
||||
|
||||
func writeQuoted(b *strings.Builder, s string) {
|
||||
last := 0
|
||||
b.WriteByte('"')
|
||||
for i, r := range s {
|
||||
if !isQtext(r) && !isWSP(r) {
|
||||
if i > last {
|
||||
b.WriteString(s[last:i])
|
||||
}
|
||||
b.WriteByte('\\')
|
||||
b.WriteRune(r)
|
||||
last = i + utf8.RuneLen(r)
|
||||
}
|
||||
}
|
||||
if last < len(s) {
|
||||
b.WriteString(s[last:])
|
||||
}
|
||||
b.WriteByte('"')
|
||||
}
|
||||
|
||||
func formatAddress(name, email string) string {
|
||||
// somewhat based on stdlib' mail.Address.String()
|
||||
|
||||
b := &strings.Builder{}
|
||||
|
||||
if name != "" {
|
||||
needsEncoding := false
|
||||
needsQuoting := false
|
||||
for _, r := range name {
|
||||
if r >= 0x80 || (!isWSP(r) && !isVchar(r)) {
|
||||
needsEncoding = true
|
||||
break
|
||||
}
|
||||
if !isAtext(r) {
|
||||
needsQuoting = true
|
||||
}
|
||||
}
|
||||
|
||||
if needsEncoding {
|
||||
// Text in an encoded-word in a display-name must not contain certain
|
||||
// characters like quotes or parentheses (see RFC 2047 section 5.3).
|
||||
// When this is the case encode the name using base64 encoding.
|
||||
if strings.ContainsAny(name, "\"#$%&'(),.:;<>@[]^`{|}~") {
|
||||
b.WriteString(mime.BEncoding.Encode("utf-8", name))
|
||||
} else {
|
||||
b.WriteString(mime.QEncoding.Encode("utf-8", name))
|
||||
}
|
||||
} else if needsQuoting {
|
||||
writeQuoted(b, name)
|
||||
} else {
|
||||
b.WriteString(name)
|
||||
}
|
||||
|
||||
b.WriteByte(' ')
|
||||
}
|
||||
|
||||
at := strings.LastIndex(email, "@")
|
||||
var local, domain string
|
||||
if at >= 0 {
|
||||
local, domain = email[:at], email[at+1:]
|
||||
} else {
|
||||
local = email
|
||||
}
|
||||
|
||||
quoteLocal := false
|
||||
for i, r := range local {
|
||||
if isAtext(r) {
|
||||
// if atom then okay
|
||||
continue
|
||||
}
|
||||
if r == '.' && r > 0 && local[i-1] != '.' && i < len(local)-1 {
|
||||
// dots are okay but only if surrounded by non-dots
|
||||
continue
|
||||
}
|
||||
quoteLocal = true
|
||||
break
|
||||
}
|
||||
|
||||
b.WriteByte('<')
|
||||
if !quoteLocal {
|
||||
b.WriteString(local)
|
||||
} else {
|
||||
writeQuoted(b, local)
|
||||
}
|
||||
b.WriteByte('@')
|
||||
b.WriteString(domain)
|
||||
b.WriteByte('>')
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
|
||||
type int64Sorter []int64
|
||||
|
||||
func (self int64Sorter) Len() int {
|
||||
|
Reference in New Issue
Block a user