1/*2Maddy Mail Server - Composable all-in-one email server.3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors45This program is free software: you can redistribute it and/or modify6it under the terms of the GNU General Public License as published by7the Free Software Foundation, either version 3 of the License, or8(at your option) any later version.910This program is distributed in the hope that it will be useful,11but WITHOUT ANY WARRANTY; without even the implied warranty of12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13GNU General Public License for more details.1415You should have received a copy of the GNU General Public License16along with this program. If not, see <https://www.gnu.org/licenses/>.17*/1819package smtp2021import (22 "errors"23 "fmt"24 "net/mail"25 "time"2627 "github.com/emersion/go-message/textproto"28 "github.com/foxcpp/maddy/framework/exterrors"29 "github.com/foxcpp/maddy/framework/module"30 "github.com/google/uuid"31)3233var (34 msgIDField = func() (string, error) {35 id, err := uuid.NewRandom()36 if err != nil {37 return "", err38 }39 return id.String(), nil40 }4142 now = time.Now43)4445func (s *Session) submissionPrepare(msgMeta *module.MsgMetadata, header *textproto.Header) error {46 msgMeta.DontTraceSender = true4748 if header.Get("Message-ID") == "" {49 msgId, err := msgIDField()50 if err != nil {51 return errors.New("Message-ID generation failed")52 }53 s.log.Msg("adding missing Message-ID")54 header.Set("Message-ID", "<"+msgId+"@"+s.endp.serv.Domain+">")55 }5657 if header.Get("From") == "" {58 return &exterrors.SMTPError{59 Code: 554,60 EnhancedCode: exterrors.EnhancedCode{5, 6, 0},61 Message: "Message does not contains a From header field",62 Misc: map[string]interface{}{63 "modifier": "submission_prepare",64 },65 }66 }6768 for _, hdr := range [...]string{"Sender"} {69 if value := header.Get(hdr); value != "" {70 if _, err := mail.ParseAddress(value); err != nil {71 return &exterrors.SMTPError{72 Code: 554,73 EnhancedCode: exterrors.EnhancedCode{5, 6, 0},74 Message: fmt.Sprintf("Invalid address in %s", hdr),75 Misc: map[string]interface{}{76 "modifier": "submission_prepare",77 "addr": value,78 },79 Err: err,80 }81 }82 }83 }84 for _, hdr := range [...]string{"To", "Cc", "Bcc", "Reply-To"} {85 if value := header.Get(hdr); value != "" {86 if _, err := mail.ParseAddressList(value); err != nil {87 return &exterrors.SMTPError{88 Code: 554,89 EnhancedCode: exterrors.EnhancedCode{5, 6, 0},90 Message: fmt.Sprintf("Invalid address in %s", hdr),91 Misc: map[string]interface{}{92 "modifier": "submission_prepare",93 "addr": value,94 },95 Err: err,96 }97 }98 }99 }100101 addrs, err := mail.ParseAddressList(header.Get("From"))102 if err != nil {103 return &exterrors.SMTPError{104 Code: 554,105 EnhancedCode: exterrors.EnhancedCode{5, 6, 0},106 Message: "Invalid address in From",107 Misc: map[string]interface{}{108 "modifier": "submission_prepare",109 "addr": header.Get("From"),110 },111 Err: err,112 }113 }114115 // https://tools.ietf.org/html/rfc5322#section-3.6.2116 // If From contains multiple addresses, Sender field must be present.117 if len(addrs) > 1 && header.Get("Sender") == "" {118 return &exterrors.SMTPError{119 Code: 554,120 EnhancedCode: exterrors.EnhancedCode{5, 6, 0},121 Message: "Missing Sender header field",122 Misc: map[string]interface{}{123 "modifier": "submission_prepare",124 "from": header.Get("From"),125 },126 }127 }128129 if dateHdr := header.Get("Date"); dateHdr != "" {130 _, err := parseMessageDateTime(dateHdr)131 if err != nil {132 return &exterrors.SMTPError{133 Code: 554,134 Message: "Malformed Date header",135 Misc: map[string]interface{}{136 "modifier": "submission_prepare",137 "date": dateHdr,138 },139 Err: err,140 }141 }142 } else {143 s.log.Msg("adding missing Date header")144 header.Set("Date", now().UTC().Format("Mon, 2 Jan 2006 15:04:05 -0700"))145 }146147 return nil148}