maddy

Fork https://github.com/foxcpp/maddy

git clone git://git.lin.moe/go/maddy.git

  1/*
  2Maddy Mail Server - Composable all-in-one email server.
  3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors
  4
  5This program is free software: you can redistribute it and/or modify
  6it under the terms of the GNU General Public License as published by
  7the Free Software Foundation, either version 3 of the License, or
  8(at your option) any later version.
  9
 10This program is distributed in the hope that it will be useful,
 11but WITHOUT ANY WARRANTY; without even the implied warranty of
 12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13GNU General Public License for more details.
 14
 15You should have received a copy of the GNU General Public License
 16along with this program.  If not, see <https://www.gnu.org/licenses/>.
 17*/
 18
 19package smtp
 20
 21import (
 22	"errors"
 23	"fmt"
 24	"net/mail"
 25	"time"
 26
 27	"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)
 32
 33var (
 34	msgIDField = func() (string, error) {
 35		id, err := uuid.NewRandom()
 36		if err != nil {
 37			return "", err
 38		}
 39		return id.String(), nil
 40	}
 41
 42	now = time.Now
 43)
 44
 45func (s *Session) submissionPrepare(msgMeta *module.MsgMetadata, header *textproto.Header) error {
 46	msgMeta.DontTraceSender = true
 47
 48	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	}
 56
 57	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	}
 67
 68	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	}
100
101	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	}
114
115	// https://tools.ietf.org/html/rfc5322#section-3.6.2
116	// 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	}
128
129	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	}
146
147	return nil
148}