Compare commits

...

2 Commits

Author SHA1 Message Date
Guilherme Rugai Freire
b5f2e37ff8
update golang and deps 2025-08-28 10:13:19 -03:00
Guilherme Rugai Freire
a8860ea560
add support for cc and bcc
now it can handle when mails are carbon-copied
fixes #1 fixes #2
2025-08-28 09:46:20 -03:00
6 changed files with 57 additions and 29 deletions

View File

@ -1,4 +1,4 @@
FROM golang:1.22 as build
FROM golang:1.23 as build
RUN apt-get update && apt-get upgrade -y
RUN apt-get install make sqlite3 -y

8
go.mod
View File

@ -1,9 +1,11 @@
module github.com/GRFreire/nthmail
go 1.21.6
go 1.23.0
toolchain go1.23.5
require (
github.com/a-h/templ v0.2.747 // indirect
github.com/a-h/templ v0.3.943 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 // indirect
github.com/emersion/go-smtp v0.20.2 // indirect
@ -12,5 +14,5 @@ require (
github.com/mattn/go-sqlite3 v1.14.22 // indirect
github.com/microcosm-cc/bluemonday v1.0.27 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/net v0.42.0 // indirect
)

4
go.sum
View File

@ -4,6 +4,8 @@ github.com/a-h/templ v0.2.598 h1:6jMIHv6wQZvdPxTuv87erW4RqN/FPU0wk7ZHN5wVuuo=
github.com/a-h/templ v0.2.598/go.mod h1:SA7mtYwVEajbIXFRh3vKdYm/4FYyLQAtPH1+KxzGPA8=
github.com/a-h/templ v0.2.747 h1:D0dQ2lxC3W7Dxl6fxQ/1zZHBQslSkTSvl5FxP/CfdKg=
github.com/a-h/templ v0.2.747/go.mod h1:69ObQIbrcuwPCU32ohNaWce3Cb7qM5GMiqN1K+2yop4=
github.com/a-h/templ v0.3.943 h1:o+mT/4yqhZ33F3ootBiHwaY4HM5EVaOJfIshvd5UNTY=
github.com/a-h/templ v0.3.943/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ=
@ -28,3 +30,5 @@ golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=

View File

@ -40,13 +40,13 @@ type Session struct {
domain string
}
func get_addr_domain(addr string) string {
index := strings.Index(addr, "@")
if index < 0 {
return ""
func append_addrs_with_domain(addrs []string, domain string, with_domain *[]string) {
for _, a := range addrs {
index := strings.Index(a, "@")
if index > 0 && a[index+1:] == domain {
*with_domain = append(*with_domain, a)
}
}
return addr[index+1:]
}
func (session *Session) AuthPlain(username, password string) error {
@ -61,9 +61,6 @@ func (session *Session) Mail(from string, opts *smtp.MailOptions) error {
}
func (session *Session) Rcpt(to string, opts *smtp.RcptOptions) error {
if get_addr_domain(to) != session.domain {
return errors.New("To addr domain is not available in this server")
}
session.rcpt = to
return nil
@ -82,20 +79,28 @@ func (session *Session) Data(reader io.Reader) error {
return err
}
if get_addr_domain(mail_obj.To) != session.domain {
return errors.New("To addr domain is not available in this server")
var addrs []string
append_addrs_with_domain(mail_obj.To, session.domain, &addrs)
append_addrs_with_domain(mail_obj.Cc, session.domain, &addrs)
append_addrs_with_domain(mail_obj.Bcc, session.domain, &addrs)
if len(addrs) <= 0 {
return errors.New("Not a single addr from to, cc and cc has the domain available in this server")
}
stmt, err := session.tx.Prepare("INSERT INTO mails (arrived_at, rcpt_addr, from_addr, subject, data) VALUES (?, ?, ?, ?, ?)")
if err != nil {
println(err)
return err
}
defer stmt.Close()
for _, addr := range addrs {
stmt, err := session.tx.Prepare("INSERT INTO mails (arrived_at, rcpt_addr, from_addr, subject, data) VALUES (?, ?, ?, ?, ?)")
if err != nil {
println(err)
return err
}
defer stmt.Close()
_, err = stmt.Exec(session.arrived_at, addr, mail_obj.From, mail_obj.Subject, bytes)
if err != nil {
return err
}
_, err = stmt.Exec(session.arrived_at, session.rcpt, mail_obj.From, mail_obj.Subject, bytes)
if err != nil {
return err
}
err = session.tx.Commit()

View File

@ -38,8 +38,9 @@ type Mail_obj struct {
Id int
From string
Date time.Time
To string
Bcc string
To []string
Cc []string
Bcc []string
Subject string
Body []Mail_body
@ -79,10 +80,26 @@ func Parse_mail(m_data []byte, header_only bool) (Mail_obj, error) {
// HEADERS
dec := new(mime.WordDecoder)
m.From, _ = dec.DecodeHeader(mail_msg.Header.Get("From"))
m.To, _ = dec.DecodeHeader(mail_msg.Header.Get("To"))
m.Bcc, _ = dec.DecodeHeader(mail_msg.Header.Get("Bcc"))
m.Subject, _ = dec.DecodeHeader(mail_msg.Header.Get("Subject"))
to_addrs, _ := mail_msg.Header.AddressList("To")
m.To = make([]string, len(to_addrs))
for i, a := range to_addrs {
m.To[i] = a.Address
}
cc_addrs, _ := mail_msg.Header.AddressList("Cc")
m.Cc = make([]string, len(to_addrs))
for i, a := range cc_addrs {
m.Cc[i] = a.Address
}
bcc_addrs, _ := mail_msg.Header.AddressList("Bcc")
m.Bcc = make([]string, len(to_addrs))
for i, a := range bcc_addrs {
m.Bcc[i] = a.Address
}
if header_only {
return m, nil
}

View File

@ -159,7 +159,7 @@ func (sr ServerResouces) handleInbox(res http.ResponseWriter, req *http.Request)
var mail_obj mail_utils.Mail_obj
mail_obj.Id = m.Id
mail_obj.Date = time.Unix(m.Arrived_at, 0)
mail_obj.To = m.Rcpt_addr
mail_obj.To = []string{m.Rcpt_addr}
mail_obj.From = m.From_addr
mail_obj.Subject = m.Subject