Browse Source

refactoring: protocol types gets a formal type & other small renames

flashmob 5 years ago
parent
commit
210353dc5a
8 changed files with 128 additions and 39 deletions
  1. 1 8
      backends/p_guerrilla_db_redis.go
  2. 1 8
      backends/p_header.go
  3. 1 1
      chunk/processor.go
  4. 20 5
      chunk/store.go
  5. 28 11
      chunk/store_memory.go
  6. 21 6
      chunk/store_sql.go
  7. 41 0
      mail/envelope.go
  8. 15 0
      mail/smtp/parse.go

+ 1 - 8
backends/p_guerrilla_db_redis.go

@@ -440,17 +440,10 @@ func GuerrillaDbRedis() Decorator {
 				e.QueuedId = hash
 				e.QueuedId = hash
 
 
 				// Add extra headers
 				// Add extra headers
-				protocol := "SMTP"
-				if e.ESMTP {
-					protocol = "E" + protocol
-				}
-				if e.TLS {
-					protocol = protocol + "S"
-				}
 				var addHead string
 				var addHead string
 				addHead += "Delivered-To: " + to + "\r\n"
 				addHead += "Delivered-To: " + to + "\r\n"
 				addHead += "Received: from " + e.RemoteIP + " ([" + e.RemoteIP + "])\r\n"
 				addHead += "Received: from " + e.RemoteIP + " ([" + e.RemoteIP + "])\r\n"
-				addHead += "	by " + e.RcptTo[0].Host + " with " + protocol + " id " + hash + "@" + e.RcptTo[0].Host + ";\r\n"
+				addHead += "	by " + e.RcptTo[0].Host + " with " + e.Protocol().String() + " id " + hash + "@" + e.RcptTo[0].Host + ";\r\n"
 				addHead += "	" + time.Now().Format(time.RFC1123Z) + "\r\n"
 				addHead += "	" + time.Now().Format(time.RFC1123Z) + "\r\n"
 
 
 				// data will be compressed when printed, with addHead added to beginning
 				// data will be compressed when printed, with addHead added to beginning

+ 1 - 8
backends/p_header.go

@@ -54,18 +54,11 @@ func Header() Decorator {
 				if len(e.Hashes) > 0 {
 				if len(e.Hashes) > 0 {
 					hash = e.Hashes[0]
 					hash = e.Hashes[0]
 				}
 				}
-				protocol := "SMTP"
-				if e.ESMTP {
-					protocol = "E" + protocol
-				}
-				if e.TLS {
-					protocol = protocol + "S"
-				}
 				var addHead string
 				var addHead string
 				addHead += "Delivered-To: " + to + "\n"
 				addHead += "Delivered-To: " + to + "\n"
 				addHead += "Received: from " + e.RemoteIP + " ([" + e.RemoteIP + "])\n"
 				addHead += "Received: from " + e.RemoteIP + " ([" + e.RemoteIP + "])\n"
 				if len(e.RcptTo) > 0 {
 				if len(e.RcptTo) > 0 {
-					addHead += "	by " + e.RcptTo[0].Host + " with " + protocol + " id " + hash + "@" + e.RcptTo[0].Host + ";\n"
+					addHead += "	by " + e.RcptTo[0].Host + " with " + e.Protocol().String() + " id " + hash + "@" + e.RcptTo[0].Host + ";\n"
 				}
 				}
 				addHead += "	" + time.Now().Format(time.RFC1123Z) + "\n"
 				addHead += "	" + time.Now().Format(time.RFC1123Z) + "\n"
 				// save the result
 				// save the result

+ 1 - 1
chunk/processor.go

@@ -157,7 +157,7 @@ func Chunksaver() *backends.StreamDecorator {
 					e.RcptTo[0].String(),
 					e.RcptTo[0].String(),
 					ip,
 					ip,
 					e.MailFrom.String(),
 					e.MailFrom.String(),
-					e.TLS,
+					e.Protocol(),
 					e.TransportType,
 					e.TransportType,
 				)
 				)
 				if err != nil {
 				if err != nil {

+ 20 - 5
chunk/store.go

@@ -2,6 +2,7 @@ package chunk
 
 
 import (
 import (
 	"github.com/flashmob/go-guerrilla/backends"
 	"github.com/flashmob/go-guerrilla/backends"
+	"github.com/flashmob/go-guerrilla/mail"
 	"github.com/flashmob/go-guerrilla/mail/smtp"
 	"github.com/flashmob/go-guerrilla/mail/smtp"
 	"io"
 	"io"
 	"net"
 	"net"
@@ -15,9 +16,23 @@ func init() {
 // Storage defines an interface to the storage layer (the database)
 // Storage defines an interface to the storage layer (the database)
 type Storage interface {
 type Storage interface {
 	// OpenMessage is used to begin saving an email. An email id is returned and used to call CloseMessage later
 	// OpenMessage is used to begin saving an email. An email id is returned and used to call CloseMessage later
-	OpenMessage(from string, helo string, recipient string, ipAddress net.IPAddr, returnPath string, isTLS bool, transport smtp.TransportType) (mailID uint64, err error)
+	OpenMessage(
+		from string,
+		helo string,
+		recipient string,
+		ipAddress net.IPAddr,
+		returnPath string,
+		protocol mail.Protocol,
+		transport smtp.TransportType) (mailID uint64, err error)
 	// CloseMessage finalizes the writing of an email. Additional data collected while parsing the email is saved
 	// CloseMessage finalizes the writing of an email. Additional data collected while parsing the email is saved
-	CloseMessage(mailID uint64, size int64, partsInfo *PartsInfo, subject string, deliveryID string, to string, from string) error
+	CloseMessage(
+		mailID uint64,
+		size int64,
+		partsInfo *PartsInfo,
+		subject string,
+		queuedID string,
+		to string,
+		from string) error
 	// AddChunk saves a chunk of bytes to a given hash key
 	// AddChunk saves a chunk of bytes to a given hash key
 	AddChunk(data []byte, hash []byte) error
 	AddChunk(data []byte, hash []byte) error
 	// GetEmail returns an email that's been saved
 	// GetEmail returns an email that's been saved
@@ -47,13 +62,13 @@ type Email struct {
 	partsInfo  PartsInfo
 	partsInfo  PartsInfo
 	helo       string // helo message given by the client when the message was transmitted
 	helo       string // helo message given by the client when the message was transmitted
 	subject    string // subject stores the value from the first "Subject" header field
 	subject    string // subject stores the value from the first "Subject" header field
-	deliveryID string
+	queuedID   string
 	recipient  string             // recipient is the email address that the server received from the RCPT TO command
 	recipient  string             // recipient is the email address that the server received from the RCPT TO command
 	ipv4       net.IPAddr         // set to a value if client connected via ipv4
 	ipv4       net.IPAddr         // set to a value if client connected via ipv4
 	ipv6       net.IPAddr         // set to a value if client connected via ipv6
 	ipv6       net.IPAddr         // set to a value if client connected via ipv6
 	returnPath string             // returnPath is the email address that the server received from the MAIL FROM command
 	returnPath string             // returnPath is the email address that the server received from the MAIL FROM command
-	isTLS      bool               // isTLS is true when TLS was used to connect
-	transport  smtp.TransportType // did the sender signal 8bitmime?
+	protocol   mail.Protocol      // protocol such as SMTP, ESTMP, ESMTPS
+	transport  smtp.TransportType // transport what type of transport the message uses, eg 8bitmime
 }
 }
 
 
 type Chunk struct {
 type Chunk struct {

+ 28 - 11
chunk/store_memory.go

@@ -5,6 +5,7 @@ import (
 	"compress/zlib"
 	"compress/zlib"
 	"errors"
 	"errors"
 	"github.com/flashmob/go-guerrilla/backends"
 	"github.com/flashmob/go-guerrilla/backends"
+	"github.com/flashmob/go-guerrilla/mail"
 	"github.com/flashmob/go-guerrilla/mail/smtp"
 	"github.com/flashmob/go-guerrilla/mail/smtp"
 	"net"
 	"net"
 	"time"
 	"time"
@@ -20,6 +21,7 @@ type storeMemoryConfig struct {
 	CompressLevel int `json:"compress_level,omitempty"`
 	CompressLevel int `json:"compress_level,omitempty"`
 }
 }
 
 
+// A StoreMemory stores emails and chunked data in mememory
 type StoreMemory struct {
 type StoreMemory struct {
 	chunks        map[HashKey]*memoryChunk
 	chunks        map[HashKey]*memoryChunk
 	emails        []*memoryEmail
 	emails        []*memoryEmail
@@ -38,13 +40,13 @@ type memoryEmail struct {
 	partsInfo  []byte
 	partsInfo  []byte
 	helo       string
 	helo       string
 	subject    string
 	subject    string
-	deliveryID string
+	queuedID   string
 	recipient  string
 	recipient  string
 	ipv4       net.IPAddr
 	ipv4       net.IPAddr
 	ipv6       net.IPAddr
 	ipv6       net.IPAddr
 	returnPath string
 	returnPath string
-	isTLS      bool
-	is8Bit     smtp.TransportType
+	transport  smtp.TransportType
+	protocol   mail.Protocol
 }
 }
 
 
 type memoryChunk struct {
 type memoryChunk struct {
@@ -54,7 +56,15 @@ type memoryChunk struct {
 }
 }
 
 
 // OpenMessage implements the Storage interface
 // OpenMessage implements the Storage interface
-func (m *StoreMemory) OpenMessage(from string, helo string, recipient string, ipAddress net.IPAddr, returnPath string, isTLS bool, transport smtp.TransportType) (mailID uint64, err error) {
+func (m *StoreMemory) OpenMessage(
+	from string,
+	helo string,
+	recipient string,
+	ipAddress net.IPAddr,
+	returnPath string,
+	protocol mail.Protocol,
+	transport smtp.TransportType,
+) (mailID uint64, err error) {
 	var ip4, ip6 net.IPAddr
 	var ip4, ip6 net.IPAddr
 	if ip := ipAddress.IP.To4(); ip != nil {
 	if ip := ipAddress.IP.To4(); ip != nil {
 		ip4 = ipAddress
 		ip4 = ipAddress
@@ -70,8 +80,8 @@ func (m *StoreMemory) OpenMessage(from string, helo string, recipient string, ip
 		ipv4:       ip4,
 		ipv4:       ip4,
 		ipv6:       ip6,
 		ipv6:       ip6,
 		returnPath: returnPath,
 		returnPath: returnPath,
-		isTLS:      isTLS,
-		is8Bit:     transport,
+		transport:  transport,
+		protocol:   protocol,
 	}
 	}
 	m.emails = append(m.emails, &email)
 	m.emails = append(m.emails, &email)
 	m.nextID++
 	m.nextID++
@@ -79,7 +89,14 @@ func (m *StoreMemory) OpenMessage(from string, helo string, recipient string, ip
 }
 }
 
 
 // CloseMessage implements the Storage interface
 // CloseMessage implements the Storage interface
-func (m *StoreMemory) CloseMessage(mailID uint64, size int64, partsInfo *PartsInfo, subject string, deliveryID string, to string, from string) error {
+func (m *StoreMemory) CloseMessage(
+	mailID uint64,
+	size int64,
+	partsInfo *PartsInfo,
+	subject string,
+	queuedID string,
+	to string,
+	from string) error {
 	if email := m.emails[mailID-m.offset]; email == nil {
 	if email := m.emails[mailID-m.offset]; email == nil {
 		return errors.New("email not found")
 		return errors.New("email not found")
 	} else {
 	} else {
@@ -90,7 +107,7 @@ func (m *StoreMemory) CloseMessage(mailID uint64, size int64, partsInfo *PartsIn
 			email.partsInfo = info
 			email.partsInfo = info
 		}
 		}
 		email.subject = subject
 		email.subject = subject
-		email.deliveryID = deliveryID
+		email.queuedID = queuedID
 		email.to = to
 		email.to = to
 		email.from = from
 		email.from = from
 		email.size = size
 		email.size = size
@@ -179,13 +196,13 @@ func (m *StoreMemory) GetEmail(mailID uint64) (*Email, error) {
 		partsInfo:  *pi,
 		partsInfo:  *pi,
 		helo:       email.helo,
 		helo:       email.helo,
 		subject:    email.subject,
 		subject:    email.subject,
-		deliveryID: email.deliveryID,
+		queuedID:   email.queuedID,
 		recipient:  email.recipient,
 		recipient:  email.recipient,
 		ipv4:       email.ipv4,
 		ipv4:       email.ipv4,
 		ipv6:       email.ipv6,
 		ipv6:       email.ipv6,
 		returnPath: email.returnPath,
 		returnPath: email.returnPath,
-		isTLS:      email.isTLS,
-		transport:  email.is8Bit,
+		transport:  email.transport,
+		protocol:   email.protocol,
 	}, nil
 	}, nil
 }
 }
 
 

+ 21 - 6
chunk/store_sql.go

@@ -5,6 +5,7 @@ import (
 	"encoding/binary"
 	"encoding/binary"
 	"encoding/json"
 	"encoding/json"
 	"github.com/flashmob/go-guerrilla/backends"
 	"github.com/flashmob/go-guerrilla/backends"
+	"github.com/flashmob/go-guerrilla/mail"
 	"github.com/flashmob/go-guerrilla/mail/smtp"
 	"github.com/flashmob/go-guerrilla/mail/smtp"
 	"net"
 	"net"
 )
 )
@@ -53,7 +54,7 @@ func (s *StoreSQL) prepareSql() error {
 	// begin inserting an email (before saving chunks)
 	// begin inserting an email (before saving chunks)
 	if stmt, err := s.db.Prepare(`INSERT INTO ` +
 	if stmt, err := s.db.Prepare(`INSERT INTO ` +
 		s.config.EmailTable +
 		s.config.EmailTable +
-		` (from, helo, recipient, ipv4_addr, ipv6_addr, return_path, is_tls, is_8bit) 
+		` (from, helo, recipient, ipv4_addr, ipv6_addr, return_path, transport, protocol) 
  VALUES(?, ?, ?, ?, ?, ?, ?, ?)`); err != nil {
  VALUES(?, ?, ?, ?, ?, ?, ?, ?)`); err != nil {
 		return err
 		return err
 	} else {
 	} else {
@@ -73,7 +74,7 @@ func (s *StoreSQL) prepareSql() error {
 	// finalize the email (the connection closed)
 	// finalize the email (the connection closed)
 	if stmt, err := s.db.Prepare(`
 	if stmt, err := s.db.Prepare(`
 		UPDATE ` + s.config.EmailTable + ` 
 		UPDATE ` + s.config.EmailTable + ` 
-			SET size=?, parts_info = ?, subject, delivery_id = ?, to = ? 
+			SET size=?, parts_info = ?, subject, queued_id = ?, to = ? 
 		WHERE mail_id = ? `); err != nil {
 		WHERE mail_id = ? `); err != nil {
 		return err
 		return err
 	} else {
 	} else {
@@ -131,7 +132,15 @@ func (s *StoreSQL) prepareSql() error {
 }
 }
 
 
 // OpenMessage implements the Storage interface
 // OpenMessage implements the Storage interface
-func (s *StoreSQL) OpenMessage(from string, helo string, recipient string, ipAddress net.IPAddr, returnPath string, isTLS bool, transport smtp.TransportType) (mailID uint64, err error) {
+func (s *StoreSQL) OpenMessage(
+	from string,
+	helo string,
+	recipient string,
+	ipAddress net.IPAddr,
+	returnPath string,
+	protocol mail.Protocol,
+	transport smtp.TransportType,
+) (mailID uint64, err error) {
 
 
 	// if it's ipv4 then we want ipv6 to be 0, and vice-versa
 	// if it's ipv4 then we want ipv6 to be 0, and vice-versa
 	var ip4 uint32
 	var ip4 uint32
@@ -141,7 +150,7 @@ func (s *StoreSQL) OpenMessage(from string, helo string, recipient string, ipAdd
 	} else {
 	} else {
 		_ = copy(ip6, ipAddress.IP)
 		_ = copy(ip6, ipAddress.IP)
 	}
 	}
-	r, err := s.statements["insertEmail"].Exec(from, helo, recipient, ip4, ip6, returnPath, isTLS, transport)
+	r, err := s.statements["insertEmail"].Exec(from, helo, recipient, ip4, ip6, returnPath, transport, protocol)
 	if err != nil {
 	if err != nil {
 		return 0, err
 		return 0, err
 	}
 	}
@@ -174,12 +183,18 @@ func (s *StoreSQL) AddChunk(data []byte, hash []byte) error {
 }
 }
 
 
 // CloseMessage implements the Storage interface
 // CloseMessage implements the Storage interface
-func (s *StoreSQL) CloseMessage(mailID uint64, size int64, partsInfo *PartsInfo, subject string, deliveryID string, to string, from string) error {
+func (s *StoreSQL) CloseMessage(
+	mailID uint64,
+	size int64,
+	partsInfo *PartsInfo,
+	subject string,
+	queuedID string,
+	to string, from string) error {
 	partsInfoJson, err := json.Marshal(partsInfo)
 	partsInfoJson, err := json.Marshal(partsInfo)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	_, err = s.statements["finalizeEmail"].Exec(size, partsInfoJson, subject, deliveryID, to, mailID)
+	_, err = s.statements["finalizeEmail"].Exec(size, partsInfoJson, subject, queuedID, to, mailID)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 41 - 0
mail/envelope.go

@@ -277,6 +277,47 @@ func (e *Envelope) PopRcpt() Address {
 	return ret
 	return ret
 }
 }
 
 
+func (e *Envelope) Protocol() Protocol {
+	protocol := ProtocolSMTP
+	switch {
+	case !e.ESMTP && !e.TLS:
+		protocol = ProtocolSMTP
+	case !e.ESMTP && e.TLS:
+		protocol = ProtocolSMTPS
+	case e.ESMTP && !e.TLS:
+		protocol = ProtocolESMTP
+	case e.ESMTP && e.TLS:
+		protocol = ProtocolESMTPS
+	}
+	return protocol
+}
+
+type Protocol int
+
+const (
+	ProtocolSMTP Protocol = iota
+	ProtocolSMTPS
+	ProtocolESMTP
+	ProtocolESMTPS
+	ProtocolLTPS
+)
+
+func (p Protocol) String() string {
+	switch p {
+	case ProtocolSMTP:
+		return "SMTP"
+	case ProtocolSMTPS:
+		return "SMTPS"
+	case ProtocolESMTP:
+		return "ESMTP"
+	case ProtocolESMTPS:
+		return "ESMTPS"
+	case ProtocolLTPS:
+		return "LTPS"
+	}
+	return "unknown"
+}
+
 const (
 const (
 	statePlainText = iota
 	statePlainText = iota
 	stateStartEncodedWord
 	stateStartEncodedWord

+ 15 - 0
mail/smtp/parse.go

@@ -24,6 +24,7 @@ const (
 
 
 type PathParam []string
 type PathParam []string
 
 
+// A TransportType specifies the message transport according to https://tools.ietf.org/html/rfc6152
 type TransportType int
 type TransportType int
 
 
 const (
 const (
@@ -33,6 +34,20 @@ const (
 	TransportTypeInvalid
 	TransportTypeInvalid
 )
 )
 
 
+func (t TransportType) String() string {
+	switch t {
+	case TransportType7bit:
+		return "7bit"
+	case TransportType8bit:
+		return "8bit"
+	case TransportTypeUnspecified:
+		return "unknown"
+	case TransportTypeInvalid:
+		return "invalid"
+	}
+	return "invalid"
+}
+
 // is8BitMime checks for the BODY parameter as
 // is8BitMime checks for the BODY parameter as
 func (p PathParam) Transport() TransportType {
 func (p PathParam) Transport() TransportType {
 	if len(p) != 2 {
 	if len(p) != 2 {