diff options
| author | Bobby <[email protected]> | 2026-03-07 17:59:31 +0530 |
|---|---|---|
| committer | Bobby <[email protected]> | 2026-03-07 17:59:31 +0530 |
| commit | a6ec9a807df68978bf3b85314a4c54c60ecc2b7a (patch) | |
| tree | 6201e8d594a3c368cce50ebc402f248814e2025f /utils/smtp | |
| parent | 57df54999e778887e66775481dab46191b46d0b6 (diff) | |
| download | dove-a6ec9a807df68978bf3b85314a4c54c60ecc2b7a.tar.xz dove-a6ec9a807df68978bf3b85314a4c54c60ecc2b7a.zip | |
feat: implement SMTP server with authentication, port validation, and email storage
Diffstat (limited to 'utils/smtp')
| -rw-r--r-- | utils/smtp/constants.go | 5 | ||||
| -rw-r--r-- | utils/smtp/server.go | 56 | ||||
| -rw-r--r-- | utils/smtp/session.go | 61 | ||||
| -rw-r--r-- | utils/smtp/storage.go | 22 | ||||
| -rw-r--r-- | utils/smtp/types.go | 13 |
5 files changed, 157 insertions, 0 deletions
diff --git a/utils/smtp/constants.go b/utils/smtp/constants.go new file mode 100644 index 0000000..cc6d173 --- /dev/null +++ b/utils/smtp/constants.go @@ -0,0 +1,5 @@ +package smtp + +const ( + LOG_PREFIX = "SMTP" +) diff --git a/utils/smtp/server.go b/utils/smtp/server.go new file mode 100644 index 0000000..0092c16 --- /dev/null +++ b/utils/smtp/server.go @@ -0,0 +1,56 @@ +package smtp + +import ( + "dove/config" + "dove/messages" + "dove/utils/logger" + "fmt" + "time" + + gosmtp "github.com/emersion/go-smtp" +) + +var activeServers []serverInstance + +func Start() { + plainAddress := fmt.Sprintf("%s:%d", config.SMTP.Host, config.SMTP.Port) + plainServer := createServer(plainAddress) + + activeServers = append(activeServers, serverInstance{server: plainServer, label: LOG_PREFIX}) + + go startListener(plainServer, LOG_PREFIX, plainAddress) +} + +func Shutdown() { + for _, instance := range activeServers { + if shutdownError := instance.server.Close(); shutdownError != nil { + logger.Errorf(LOG_PREFIX, messages.SMTPShutdownFailed, instance.label, shutdownError) + } + } + + logger.Infof(LOG_PREFIX, messages.SMTPShutdownComplete) +} + +func createServer(address string) *gosmtp.Server { + smtpServer := gosmtp.NewServer(gosmtp.BackendFunc(func(connection *gosmtp.Conn) (gosmtp.Session, error) { + logger.Debugf(LOG_PREFIX, messages.SMTPSessionStarted, connection.Hostname()) + return &session{}, nil + })) + + smtpServer.Addr = address + smtpServer.Domain = config.SMTP.Domain + smtpServer.ReadTimeout = time.Duration(config.SMTP.ReadTimeout) * time.Second + smtpServer.WriteTimeout = time.Duration(config.SMTP.WriteTimeout) * time.Second + smtpServer.MaxMessageBytes = int64(config.SMTP.MaxMessageSize) + smtpServer.AllowInsecureAuth = true + + return smtpServer +} + +func startListener(smtpServer *gosmtp.Server, label string, address string) { + logger.Successf(LOG_PREFIX, messages.SMTPServerStarting, label, address) + + if listenError := smtpServer.ListenAndServe(); listenError != nil { + logger.Fatalf(LOG_PREFIX, messages.SMTPListenFailed, label, listenError) + } +} diff --git a/utils/smtp/session.go b/utils/smtp/session.go new file mode 100644 index 0000000..029c257 --- /dev/null +++ b/utils/smtp/session.go @@ -0,0 +1,61 @@ +package smtp + +import ( + "dove/config" + "dove/messages" + "dove/utils/logger" + "dove/utils/errors" + "io" + + gosmtp "github.com/emersion/go-smtp" +) + +func (self *session) AuthPlain(username string, password string) error { + if !config.SMTP.AuthRequired { + return nil + } + + if username != config.SMTP.Username || password != config.SMTP.Password { + logger.Warnf(LOG_PREFIX, messages.SMTPAuthFailed, username) + return errors.Error(messages.SMTPInvalidCredentials) + } + + return nil +} + +func (self *session) Mail(senderAddress string, mailOptions *gosmtp.MailOptions) error { + logger.Debugf(LOG_PREFIX, messages.SMTPMailFrom, senderAddress) + self.fromAddress = senderAddress + return nil +} + +func (self *session) Rcpt(recipientAddress string, recipientOptions *gosmtp.RcptOptions) error { + logger.Debugf(LOG_PREFIX, messages.SMTPRecipient, recipientAddress) + self.toAddresses = append(self.toAddresses, recipientAddress) + return nil +} + +func (self *session) Data(messageReader io.Reader) error { + rawMessage, readError := io.ReadAll(messageReader) + if readError != nil { + return readError + } + + logger.Infof(LOG_PREFIX, messages.SMTPMessageReceived, len(rawMessage)) + + if storeError := storeMessage(self.fromAddress, self.toAddresses, rawMessage); storeError != nil { + logger.Errorf(LOG_PREFIX, messages.SMTPMessageStoreFailed, storeError) + return storeError + } + + return nil +} + +func (self *session) Reset() { + self.fromAddress = "" + self.toAddresses = nil +} + +func (self *session) Logout() error { + return nil +} diff --git a/utils/smtp/storage.go b/utils/smtp/storage.go new file mode 100644 index 0000000..b580d67 --- /dev/null +++ b/utils/smtp/storage.go @@ -0,0 +1,22 @@ +package smtp + +import ( + "dove/config" + "fmt" + "os" + "path/filepath" + "time" +) + +func storeMessage(fromAddress string, toAddresses []string, rawMessage []byte) error { + emailDirectory := filepath.Join(config.DataDir, "emails") + + if directoryError := os.MkdirAll(emailDirectory, 0750); directoryError != nil { + return directoryError + } + + filename := fmt.Sprintf("%d.eml", time.Now().UnixNano()) + filePath := filepath.Join(emailDirectory, filename) + + return os.WriteFile(filePath, rawMessage, 0640) +}
\ No newline at end of file diff --git a/utils/smtp/types.go b/utils/smtp/types.go new file mode 100644 index 0000000..38c6504 --- /dev/null +++ b/utils/smtp/types.go @@ -0,0 +1,13 @@ +package smtp + +import gosmtp "github.com/emersion/go-smtp" + +type session struct { + fromAddress string + toAddresses []string +} + +type serverInstance struct { + server *gosmtp.Server + label string +} |
