2026-04-08 21:34:29 +02:00

129 lines
4.7 KiB
Go

// SPDX-FileCopyrightText: Copyright (c) The go-mail Authors
//
// SPDX-License-Identifier: MIT
package smtp
import (
"net/textproto"
"strings"
"sync"
)
// ResponseErrorHandler provides custom handling for SMTP response errors.
//
// This interface defines a method for handling SMTP responses that do not comply with expected
// formats or behaviors. It is useful for implementing retry logic, logging, or protocol-specific
// error handling logic. It injects itself into the smtp.Client and is called whenever a server
// response does fail.
//
// Parameters:
// - host: The hostname of the SMTP server that this ResponseErrorHandler should handle
// - command: The SMTP command that triggered the error.
// - conn: The textproto.Conn to the SMTP server.
// - err: The error received from the SMTP server.
//
// Returns:
// - An error indicating the outcome of the error handling logic.
type ResponseErrorHandler interface {
HandleError(host, command string, conn *textproto.Conn, err error) error
}
// DefaultErrorHandler implements ResponseErrorHandler by returning the original error.
//
// This handler provides a default implementation of the ResponseErrorHandler interface,
// where no custom error processing is performed. It simply returns the original error
// received from the SMTP server.
//
// Returns:
// - The original error passed to the handler.
type DefaultErrorHandler struct{}
// HandleError satisfies the ResponseErrorHandler interface for the DefaultErrorHandler type
func (d *DefaultErrorHandler) HandleError(_, _ string, _ *textproto.Conn, err error) error {
return err
}
// HandlerKey uniquely identifies a host-command pair for handler mapping.
//
// This struct is used to associate a specific SMTP host and command combination with
// a ResponseErrorHandler. It enables mapping and retrieval of handlers based on
// the source of the error.
type HandlerKey struct {
Host string
Command string
}
// ErrorHandlerRegistry manages custom error handlers for SMTP host-command pairs.
//
// This struct stores mappings between HandlerKey values and corresponding
// ResponseErrorHandler implementations. It supports concurrent access and provides
// a fallback default handler when no specific match is found.
type ErrorHandlerRegistry struct {
mu sync.RWMutex
handlers map[HandlerKey]ResponseErrorHandler
defaultHandler ResponseErrorHandler
}
// NewErrorHandlerRegistry creates a new ErrorHandlerRegistry instance.
//
// This function initializes an ErrorHandlerRegistry with an empty handler map and
// assigns a DefaultErrorHandler as the fallback handler.
//
// Returns:
// - A pointer to the newly constructed ErrorHandlerRegistry.
func NewErrorHandlerRegistry() *ErrorHandlerRegistry {
return &ErrorHandlerRegistry{
handlers: make(map[HandlerKey]ResponseErrorHandler),
defaultHandler: &DefaultErrorHandler{}, // Set the default handler
}
}
// RegisterHandler associates a custom handler with a specific host and command.
//
// This method registers a ResponseErrorHandler for the given SMTP host and command
// combination. It ensures thread-safe access to the internal handler map.
//
// Parameters:
// - host: The SMTP server hostname.
// - command: The SMTP command to associate with the handler.
// - handler: The ResponseErrorHandler to register.
func (r *ErrorHandlerRegistry) RegisterHandler(host, command string, handler ResponseErrorHandler) {
r.mu.Lock()
defer r.mu.Unlock()
r.handlers[HandlerKey{Host: strings.ToLower(host), Command: strings.ToLower(command)}] = handler
}
// GetHandler retrieves the handler for a given host and command.
//
// This method returns the ResponseErrorHandler registered for the specified SMTP host
// and command. If no handler is found, it returns the default handler.
//
// Parameters:
// - host: The SMTP server hostname.
// - command: The SMTP command to look up.
//
// Returns:
// - The corresponding ResponseErrorHandler, or the default handler if none is registered.
func (r *ErrorHandlerRegistry) GetHandler(host, command string) ResponseErrorHandler {
r.mu.RLock()
defer r.mu.RUnlock()
if handler, ok := r.handlers[HandlerKey{Host: strings.ToLower(host), Command: strings.ToLower(command)}]; ok {
return handler
}
return r.defaultHandler
}
// SetDefaultHandler overrides the default ResponseErrorHandler.
//
// This method sets a new default handler to be used when no specific handler is
// registered for a host and command combination. It ensures thread-safe access.
//
// Parameters:
// - handler: The new default ResponseErrorHandler to assign.
func (r *ErrorHandlerRegistry) SetDefaultHandler(handler ResponseErrorHandler) {
r.mu.Lock()
defer r.mu.Unlock()
r.defaultHandler = handler
}