mailslurper

package
v0.0.0-...-5d7ca49 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 3, 2023 License: MIT Imports: 33 Imported by: 0

Documentation

Index

Constants

View Source
const (
	SMTP_CRLF                     string = "\r\n"
	SMTP_DATA_TERMINATOR          string = "\r\n.\r\n"
	SMTP_WELCOME_MESSAGE          string = "220 Welcome to MailSlurper!"
	SMTP_CLOSING_MESSAGE          string = "221 Bye"
	SMTP_OK_MESSAGE               string = "250 Ok"
	SMTP_DATA_RESPONSE_MESSAGE    string = "354 End data with <CR><LF>.<CR><LF>"
	SMTP_HELLO_RESPONSE_MESSAGE   string = "250 Hello. How very nice to meet you!"
	SMTP_ERROR_TRANSACTION_FAILED string = "554 Transaction failed"
)

Responses that are sent to SMTP clients

View Source
const (
	SMTP_WORKER_IDLE    SMTPWorkerState = 0
	SMTP_WORKER_WORKING SMTPWorkerState = 1
	SMTP_WORKER_DONE    SMTPWorkerState = 100
	SMTP_WORKER_ERROR   SMTPWorkerState = 101

	RECEIVE_BUFFER_LEN         = 1024
	CONNECTION_TIMEOUT_MINUTES = 10
	COMMAND_TIMEOUT_SECONDS    = 5
)

Variables

View Source
var ErrAdminCertFileNotFound = fmt.Errorf("Adminstartor certificate file not found: adminCertFile")
View Source
var ErrAdminKeyFileNotFound = fmt.Errorf("Administrator key file not found: adminKeyFile")
View Source
var ErrCertFileNotFound = fmt.Errorf("Certificate file not found: certFile")
View Source
var ErrInvalidAdminAddress = fmt.Errorf("Invalid administrator address: wwwAddress")
View Source
var ErrInvalidAuthScheme = fmt.Errorf("Invalid authentication scheme. Valid values are 'basic': authenticationScheme")
View Source
var ErrInvalidDBEngine = fmt.Errorf("Invalid DB engine. Valid values are 'SQLite', 'MySQL', 'MSSQL': dbEngine")
View Source
var ErrInvalidDBFileName = fmt.Errorf("Invalid DB file name: dbDatabase")
View Source
var ErrInvalidDBHost = fmt.Errorf("Invalid DB host: dbHost")
View Source
var ErrInvalidSMTPAddress = fmt.Errorf("Invalid SMTP address: smtpAddress")
View Source
var ErrInvalidServiceAddress = fmt.Errorf("Invalid service address: serviceAddress")
View Source
var ErrKeyFileNotFound = fmt.Errorf("Key file not found: keyFile")
View Source
var ErrMissingAuthSalt = fmt.Errorf("Missing authentication salt. A salt value is required when authentication is enabled: authSalt")
View Source
var ErrMissingAuthSecret = fmt.Errorf("Missing authentication secret. An authentication secret is requried when authentication is enabled: authSecret")
View Source
var ErrNeedAdminCertPair = fmt.Errorf("Please provide both a key file and a cert file: adminKeyFile, adminCertFile")
View Source
var ErrNeedCertPair = fmt.Errorf("Please provide both a key file and a cert file: keyFile, certFile")
View Source
var ErrNoUsersConfigured = fmt.Errorf("No users configured. When authentication is enabled you must have at least 1 valid user: credentials")
View Source
var PruneOptions = []PruneOption{
	{PruneCode("60plus"), "Older than 60 days"},
	{PruneCode("30plus"), "Older than 30 days"},
	{PruneCode("2wksplus"), "Older than 2 weeks"},
	{PruneCode("all"), "All emails"},
}

PruneOptions is the master list of options given to a user when pruning mail from the database

View Source
var SMTPCommands = map[string]SMTPCommand{
	"helo":      HELO,
	"ehlo":      HELO,
	"rcpt to":   RCPT,
	"mail from": MAIL,
	"send":      MAIL,
	"rset":      RSET,
	"quit":      QUIT,
	"data":      DATA,
	"noop":      NOOP,
}

SMTPCommands is a map of SMTP command strings to their int representation. This is primarily used because there can be more than one command to do the same things. For example, a client can send "helo" or "ehlo" to initiate the handshake.

View Source
var SMTPCommandsToStrings = map[SMTPCommand]string{
	HELO: "HELO",
	RCPT: "RCPT TO",
	MAIL: "SEND",
	RSET: "RSET",
	QUIT: "QUIT",
	DATA: "DATA",
	NOOP: "NOOP",
}

SMTPCommandsToStrings is a friendly string representations of commands. Useful in error reporting.

Functions

func GenerateID

func GenerateID() (string, error)

GenerateID creates a UUID ID for database records.

func GetCommandValue

func GetCommandValue(streamInput, command, delimiter string) (string, error)

GetCommandValue splits an input by colon (:) and returns the right hand side. If there isn't a split, or a missing colon, an InvalidCommandFormatError is returned.

func GetLogger

func GetLogger(logLevel, logFormat, who string) *logrus.Entry

GetLogger returns a logger with who owns the logger attached to it

func IsValidCommand

func IsValidCommand(streamInput, expectedCommand string) error

IsValidCommand returns an error if the input stream does not contain the expected command. The input and expected commands are lower cased, as we do not care about case when comparing.

func IsValidStorageType

func IsValidStorageType(storageType string) bool

func NeedDBHost

func NeedDBHost(storageType string) bool

func ParseDateTime

func ParseDateTime(dateString string, logger *logrus.Entry) string

ParseDateTime takes a date/time string and attempts to parse it and return a newly formatted date/time that looks like YYYY-MM-DD HH:MM:SS

func UnfoldHeaders

func UnfoldHeaders(contents string) string

UnfoldHeaders follows the RFC-2822, which defines "folding" as the process of breaking up large header lines into multiple lines. Long Subject lines or Content-Type lines (with boundaries) sometimes do this. This function will "unfold" them into a single line.

Types

type ApplyThemeRequest

type ApplyThemeRequest struct {
	Theme string `json:"theme"`
}

ApplyThemeRequest is used to update and apply a theme change

func NewApplyThemeRequest

func NewApplyThemeRequest(ctx echo.Context) (*ApplyThemeRequest, error)

NewApplyThemeRequest creates a new struct from a POST request

type Attachment

type Attachment struct {
	ID       string            `json:"id"`
	MailID   string            `json:"mailId"`
	Headers  *AttachmentHeader `json:"headers"`
	Contents string            `json:"contents"`
}

An Attachment is any content embedded in the mail data that is not considered the body

func NewAttachment

func NewAttachment(headers *AttachmentHeader, contents string) *Attachment

NewAttachment creates a new Attachment object

func (*Attachment) IsContentBase64

func (attachment *Attachment) IsContentBase64() bool

IsContentBase64 returns true/false if the content of this attachment resembles a base64 encoded string.

type AttachmentHeader

type AttachmentHeader struct {
	ContentType             string `json:"contentType"`
	MIMEVersion             string `json:"mimeVersion"`
	ContentTransferEncoding string `json:"contentTransferEncoding"`
	ContentDisposition      string `json:"contentDisposition"`
	FileName                string `json:"fileName"`
	Body                    string `json:"body"`

	Logger *logrus.Entry
}

AttachmentHeader provides information that describes an attachment. It has information such as the type of content, file name, etc...

func NewAttachmentHeader

func NewAttachmentHeader(contentType, mimeVersion, contentTransferEncoding, contentDisposition, fileName, body string) *AttachmentHeader

NewAttachmentHeader creates a new AttachmentHeader object

func (*AttachmentHeader) Parse

func (attachmentHeader *AttachmentHeader) Parse(contents string)

Parse processes a set of attachment headers. Splits lines up and figures out what header data goes into what structure key. Most headers follow this format:

Header-Name: Some value here\r\n

type Configuration

type Configuration struct {
	WWWAddress       string `json:"wwwAddress"`
	WWWPort          int    `json:"wwwPort"`
	WWWPublicURL     string `json:"wwwPublicURL"`
	ServiceAddress   string `json:"serviceAddress"`
	ServicePort      int    `json:"servicePort"`
	ServicePublicURL string `json:"servicePublicURL"`
	SMTPAddress      string `json:"smtpAddress"`
	SMTPPort         int    `json:"smtpPort"`
	DBEngine         string `json:"dbEngine"`
	DBHost           string `json:"dbHost"`
	DBPort           int    `json:"dbPort"`
	DBDatabase       string `json:"dbDatabase"`
	DBUserName       string `json:"dbUserName"`
	DBPassword       string `json:"dbPassword"`
	MaxWorkers       int    `json:"maxWorkers"`
	AutoStartBrowser bool   `json:"autoStartBrowser"`
	CertFile         string `json:"certFile"`
	KeyFile          string `json:"keyFile"`
	AdminCertFile    string `json:"adminCertFile"`
	AdminKeyFile     string `json:"adminKeyFile"`
	Theme            string `json:"theme"`

	AuthSecret           string            `json:"authSecret"`
	AuthSalt             string            `json:"authSalt"`
	AuthenticationScheme string            `json:"authenticationScheme"`
	AuthTimeoutInMinutes int               `json:"authTimeoutInMinutes"`
	Credentials          map[string]string `json:"credentials"`

	StorageType StorageType `json:"-"`
}

The Configuration structure represents a JSON configuration file with settings for how to bind servers and connect to databases.

func LoadConfiguration

func LoadConfiguration(reader io.Reader) (*Configuration, error)

LoadConfiguration reads data from a Reader into a new Configuration structure.

func LoadConfigurationFromFile

func LoadConfigurationFromFile(fileName string) (*Configuration, error)

LoadConfigurationFromFile reads data from a file into a Configuration object. Makes use of LoadConfiguration().

func (*Configuration) GetDatabaseConfiguration

func (config *Configuration) GetDatabaseConfiguration() (StorageType, *ConnectionInformation)

GetDatabaseConfiguration returns a pointer to a DatabaseConnection structure with data pulled from a Configuration structure.

func (*Configuration) GetFullSMTPBindingAddress

func (config *Configuration) GetFullSMTPBindingAddress() string

GetFullSMTPBindingAddress returns a full address and port for the MailSlurper SMTP server.

func (*Configuration) GetFullServiceAppAddress

func (config *Configuration) GetFullServiceAppAddress() string

GetFullServiceAppAddress returns a full address and port for the MailSlurper service application.

func (*Configuration) GetFullWWWBindingAddress

func (config *Configuration) GetFullWWWBindingAddress() string

GetFullWWWBindingAddress returns a full address and port for the Web application.

func (*Configuration) GetPublicServiceURL

func (config *Configuration) GetPublicServiceURL() string

GetPublicServiceURL returns a full protocol, address, and port for the MailSlurper service

func (*Configuration) GetPublicWWWURL

func (config *Configuration) GetPublicWWWURL() string

GetPublicWWWURL returns a full protocol, address and port for the web application

func (*Configuration) GetTheme

func (config *Configuration) GetTheme() string

GetTheme returns the configured theme. If there isn't one, the default theme is used

func (*Configuration) IsAdminSSL

func (config *Configuration) IsAdminSSL() bool

IsAdminSSL returns true if cert files are provided for the admin

func (*Configuration) IsServiceSSL

func (config *Configuration) IsServiceSSL() bool

IsServiceSSL returns true if cert files are provided for the SMTP server and the services tier

func (*Configuration) SaveConfiguration

func (config *Configuration) SaveConfiguration(configFile string) error

SaveConfiguration saves the current state of a Configuration structure into a JSON file.

func (*Configuration) Validate

func (config *Configuration) Validate() error

type ConnectionExistsError

type ConnectionExistsError struct {
	Address string
}

An ConnectionExistsError is used to alert a client that there is already a connection by this address cached

func ConnectionExists

func ConnectionExists(address string) *ConnectionExistsError

ConnectionExists returns a new error object

func (*ConnectionExistsError) Error

func (err *ConnectionExistsError) Error() string

type ConnectionInformation

type ConnectionInformation struct {
	Address  string
	Port     int
	Database string
	UserName string
	Password string
	Filename string
}

ConnectionInformation contains data necessary to establish a connection to a database server.

func NewConnectionInformation

func NewConnectionInformation(address string, port int) *ConnectionInformation

NewConnectionInformation returns a new ConnectionInformation structure with the address and port filled in.

func (*ConnectionInformation) SetDatabaseFile

func (information *ConnectionInformation) SetDatabaseFile(filename string)

SetDatabaseFile sets the name of a file-base database. This is used for SQLite

func (*ConnectionInformation) SetDatabaseInformation

func (information *ConnectionInformation) SetDatabaseInformation(database, userName, password string)

SetDatabaseInformation fills in the name of a database to connect to, and the user credentials necessary to do so

func (*ConnectionInformation) String

func (information *ConnectionInformation) String() string

type ConnectionManager

type ConnectionManager struct {
	// contains filtered or unexported fields
}

A ConnectionManager is responsible for maintaining, closing, and cleaning client connections. For every connection there is a worker. After an idle timeout period the manager will forceably close a client connection.

func NewConnectionManager

func NewConnectionManager(logger *logrus.Entry, config *Configuration, killServerContext context.Context, mailItemChannel chan *MailItem, serverPool *ServerPool) *ConnectionManager

NewConnectionManager creates a new struct

func (*ConnectionManager) Close

func (m *ConnectionManager) Close(connection net.Conn) error

Close will close a client connection. The state of the worker is only used for logging purposes

func (*ConnectionManager) New

func (m *ConnectionManager) New(connection net.Conn) error

New attempts to track a new client connection. The SMTPListener will use this to track a client connection and its worker

type ConnectionNotExistsError

type ConnectionNotExistsError struct {
	Address string
}

An ConnectionNotExistsError is used to alert a client that the specified connection is not in the ConnectionManager pool

func ConnectionNotExists

func ConnectionNotExists(address string) *ConnectionNotExistsError

ConnectionNotExists returns a new error object

func (*ConnectionNotExistsError) Error

func (err *ConnectionNotExistsError) Error() string

type ConnectionPool

type ConnectionPool map[string]*ConnectionPoolItem

ConnectionPool is a map of remote address to TCP connections and their workers

func NewConnectionPool

func NewConnectionPool() ConnectionPool

NewConnectionPool creates a new empty map

type ConnectionPoolItem

type ConnectionPoolItem struct {
	Connection net.Conn
	Worker     *SMTPWorker
}

ConnectionPoolItem is a single item in the pool. This tracks a connection to its worker

func NewConnectionPoolItem

func NewConnectionPoolItem(connection net.Conn, worker *SMTPWorker) *ConnectionPoolItem

NewConnectionPoolItem create a new object

type DataCommandExecutor

type DataCommandExecutor struct {
	// contains filtered or unexported fields
}

DataCommandExecutor process the Data TO command

func NewDataCommandExecutor

func NewDataCommandExecutor(
	logger *logrus.Entry,
	reader *SMTPReader,
	writer *SMTPWriter,
	emailValidationService EmailValidationProvider,
	xssService sanitizer.IXSSServiceProvider,
) *DataCommandExecutor

NewDataCommandExecutor creates a new struct

func (*DataCommandExecutor) Process

func (e *DataCommandExecutor) Process(streamInput string, mailItem *MailItem) error

Process processes the DATA command (constant DATA). When a client sends the DATA command there are three parts to the transmission content. Before this data can be processed this function will tell the client how to terminate the DATA block. We are asking clients to terminate with "\r\n.\r\n".

The first part is a set of header lines. Each header line is a header key (name), followed by a colon, followed by the value for that header key. For example a header key might be "Subject" with a value of "Testing Mail!".

After the header section there should be two sets of carriage return/line feed characters. This signals the end of the header block and the start of the message body.

Finally when the client sends the "\r\n.\r\n" the DATA transmission portion is complete. This function will return the following items.

  1. Headers (MailHeader)
  2. Body breakdown (MailBody)
  3. error structure

type DatabaseReceiver

type DatabaseReceiver struct {
	// contains filtered or unexported fields
}

A DatabaseReceiver takes a MailItem and writes it to a database

func NewDatabaseReceiver

func NewDatabaseReceiver(database IStorage, logger *logrus.Entry) DatabaseReceiver

NewDatabaseReceiver creates a new DatabaseReceiver object

func (DatabaseReceiver) Receive

func (receiver DatabaseReceiver) Receive(mailItem *MailItem) error

Receive takes a MailItem and writes it to the provided storage engine

type DeleteMailRequest

type DeleteMailRequest struct {
	PruneCode PruneCode `json:"pruneCode" form:"pruneCoe"`
}

DeleteMailRequest is used when requesting to delete mail items.

type EmailValidationProvider

type EmailValidationProvider interface {
	GetEmailComponents(email string) (*mail.Address, error)
	IsValidEmail(email string) bool
}

EmailValidationProvider is an interface for describing an email validation service.

type EmailValidationService

type EmailValidationService struct {
}

EmailValidationService realizes the EmailValidationProvider interface by offering functions for working with email validation and manipulation.

func NewEmailValidationService

func NewEmailValidationService() *EmailValidationService

NewEmailValidationService creates a new object

func (*EmailValidationService) GetEmailComponents

func (service *EmailValidationService) GetEmailComponents(email string) (*mail.Address, error)

GetEmailComponents returns an object with all the parts of a parsed email address

func (*EmailValidationService) IsValidEmail

func (service *EmailValidationService) IsValidEmail(email string) bool

IsValidEmail returns true if the provided email is valid and parses

type HelloCommandExecutor

type HelloCommandExecutor struct {
	// contains filtered or unexported fields
}

HelloCommandExecutor process the commands EHLO, HELO

func NewHelloCommandExecutor

func NewHelloCommandExecutor(logger *logrus.Entry, reader *SMTPReader, writer *SMTPWriter) *HelloCommandExecutor

NewHelloCommandExecutor creates a new struct

func (*HelloCommandExecutor) Process

func (e *HelloCommandExecutor) Process(streamInput string, mailItem *MailItem) error

Process handles the HELO greeting command

type ICommandExecutor

type ICommandExecutor interface {
	Process(streamInput string, mailItem *MailItem) error
}

ICommandExecutor describes an interface for a component that executes a command issued by the SMTP worker. A command does some type of processing, such as parse a piece of the mail stream, make replies, etc...

type IConnectionManager

type IConnectionManager interface {
	Close(connection net.Conn) error
	New(connection net.Conn) error
}

IConnectionManager describes an iterface for managing TCP connections

type IItem

type IItem interface {
	GetKey() string
	GetValues() []string
	ParseHeaderString(header string) error
}

IItem represents a single header entry. Headers describe emails, bodies, and attachments. They are in the form of "Key: Value".

type IMailItemReceiver

type IMailItemReceiver interface {
	Receive(mailItem *MailItem) error
}

An IMailItemReceiver defines an interface where the implementing object can take a MailItem and do something with it, like write to a database, etc...

type ISMTPMessagePart

type ISMTPMessagePart interface {
	AddBody(body string) error
	AddHeaders(headerSet textproto.MIMEHeader) error
	BuildMessages(body string) error
	ContentIsMultipart() (bool, error)
	GetBody() string
	GetBoundary() (string, error)
	GetBoundaryFromHeaderString(header string) (string, error)
	GetContentDisposition() string
	GetContentType() string
	GetFilenameFromContentDisposition() string
	GetHeader(key string) string
	GetMessageParts() []ISMTPMessagePart
	ParseMessages(body string, boundary string) error
}

An ISMTPMessagePart represents a single message/content from a DATA transmission from an SMTP client. This contains the headers and body content. It also contains a reference to a collection of sub-messages, if any. This allows us to support the recursive tree-like nature of the MIME protocol.

type ISet

type ISet interface {
	Get(headerName string) (IItem, error)
	ParseHeaderString(headers string) error
	ToMap() map[string][]string
	UnfoldHeaders(headers string) string
}

An ISet is a set of header items. Most emails, bodies, and attachments have more than one header to describe what the content is and how to handle it.

type IStorage

type IStorage interface {
	Connect() error
	Disconnect()
	Create() error

	GetAttachment(mailID, attachmentID string) (*Attachment, error)
	GetMailByID(id string) (*MailItem, error)
	GetMailMessageRawByID(id string) (string, error)
	GetMailCollection(offset, length int, mailSearch *MailSearch) ([]*MailItem, error)
	GetMailCount(mailSearch *MailSearch) (int, error)

	DeleteMailsAfterDate(startDate string) (int64, error)
	StoreMail(mailItem *MailItem) (string, error)
}

IStorage defines an interface for structures that need to connect to storage engines. They store and retrieve data for MailSlurper

func ConnectToStorage

func ConnectToStorage(storageType StorageType, connectionInfo *ConnectionInformation, logger *logrus.Entry) (IStorage, error)

ConnectToStorage establishes a connection to the configured database engine and returns an object.

type InvalidCommandError

type InvalidCommandError struct {
	InvalidCommand string
}

An InvalidCommandError is used to alert a client that the command passed in is invalid.

func InvalidCommand

func InvalidCommand(command string) *InvalidCommandError

InvalidCommand returns a new error object

func (*InvalidCommandError) Error

func (err *InvalidCommandError) Error() string

type InvalidCommandFormatError

type InvalidCommandFormatError struct {
	InvalidCommand string
}

An InvalidCommandFormatError is used to alert a client that the command passed in has an invalid format

func InvalidCommandFormat

func InvalidCommandFormat(command string) *InvalidCommandFormatError

InvalidCommandFormat returns a new error object

func (*InvalidCommandFormatError) Error

func (err *InvalidCommandFormatError) Error() string

type InvalidEmailError

type InvalidEmailError struct {
	Email string
}

An InvalidEmailError is used to alert a client that an email address is invalid

func InvalidEmail

func InvalidEmail(email string) *InvalidEmailError

InvalidEmail returns a new error object

func (*InvalidEmailError) Error

func (err *InvalidEmailError) Error() string

type InvalidHeaderError

type InvalidHeaderError struct {
	InvalidHeader string
}

An InvalidHeaderError is used to alert a client that a header is malformed.

func InvalidHeader

func InvalidHeader(header string) *InvalidHeaderError

InvalidHeader returns a new error object

func (*InvalidHeaderError) Error

func (err *InvalidHeaderError) Error() string

type Item

type Item struct {
	Key    string
	Values []string
}

An Item represents a single header entry. Headers describe emails, bodies, and attachments. They are in the form of "Key: Value".

func (*Item) GetKey

func (headerItem *Item) GetKey() string

GetKey returns the key for this header item

func (*Item) GetValues

func (headerItem *Item) GetValues() []string

GetValues returns the value for this header item

func (*Item) ParseHeaderString

func (headerItem *Item) ParseHeaderString(header string) error

ParseHeaderString takes a header string and parses it into a key and value(s)

type MSSQLStorage

type MSSQLStorage struct {
	// contains filtered or unexported fields
}

MSSQLStorage implements the IStorage interface

func NewMSSQLStorage

func NewMSSQLStorage(connectionInformation *ConnectionInformation, logger *logrus.Entry) *MSSQLStorage

NewMSSQLStorage creates a new storage object that interfaces to MSSQL

func (*MSSQLStorage) Connect

func (storage *MSSQLStorage) Connect() error

Connect to the database

func (*MSSQLStorage) Create

func (storage *MSSQLStorage) Create() error

Create does nothing for MSSQL

func (*MSSQLStorage) DeleteMailsAfterDate

func (storage *MSSQLStorage) DeleteMailsAfterDate(startDate string) (int64, error)

DeleteMailsAfterDate deletes all mails after a specified date

func (*MSSQLStorage) Disconnect

func (storage *MSSQLStorage) Disconnect()

Disconnect does exactly what you think it does

func (*MSSQLStorage) GetAttachment

func (storage *MSSQLStorage) GetAttachment(mailID, attachmentID string) (*Attachment, error)

GetAttachment retrieves an attachment for a given mail item

func (*MSSQLStorage) GetMailByID

func (storage *MSSQLStorage) GetMailByID(mailItemID string) (*MailItem, error)

GetMailByID retrieves a single mail item and attachment by ID

func (*MSSQLStorage) GetMailCollection

func (storage *MSSQLStorage) GetMailCollection(offset, length int, mailSearch *MailSearch) ([]*MailItem, error)

GetMailCollection retrieves a slice of mail items starting at offset and getting length number of records. This query is MSSQL 2005 and higher compatible.

func (*MSSQLStorage) GetMailCount

func (storage *MSSQLStorage) GetMailCount(mailSearch *MailSearch) (int, error)

GetMailCount returns the number of total records in the mail items table

func (*MSSQLStorage) GetMailMessageRawByID

func (storage *MSSQLStorage) GetMailMessageRawByID(mailItemID string) (string, error)

GetMailMessageRawByID retrieves a single mail item and attachment by ID

func (*MSSQLStorage) StoreMail

func (storage *MSSQLStorage) StoreMail(mailItem *MailItem) (string, error)

StoreMail writes a mail item and its attachments to the storage device. This returns the new mail ID

type MailAddressCollection

type MailAddressCollection []string

MailAddressCollection is a set of email address

func NewMailAddressCollection

func NewMailAddressCollection() MailAddressCollection

NewMailAddressCollection returns a new MailAddressCollection

func NewMailAddressCollectionFromStringList

func NewMailAddressCollectionFromStringList(addresses string) MailAddressCollection

NewMailAddressCollectionFromStringList takes a list of delimited email address and breaks it into a collection of mail addresses

type MailCollectionResponse

type MailCollectionResponse struct {
	MailItems    []*MailItem `json:"mailItems"`
	TotalPages   int         `json:"totalPages"`
	TotalRecords int         `json:"totalRecords"`
}

A MailCollectionResponse is sent in response to getting a collection of mail items.

type MailCommandExecutor

type MailCommandExecutor struct {
	// contains filtered or unexported fields
}

MailCommandExecutor process the MAIL FROM

func NewMailCommandExecutor

func NewMailCommandExecutor(
	logger *logrus.Entry,
	reader *SMTPReader,
	writer *SMTPWriter,
	emailValidationService EmailValidationProvider,
	xssService sanitizer.IXSSServiceProvider,
) *MailCommandExecutor

NewMailCommandExecutor creates a new struct

func (*MailCommandExecutor) Process

func (e *MailCommandExecutor) Process(streamInput string, mailItem *MailItem) error

Process handles the MAIL FROM command. This command tells us who the sender is

type MailCountResponse

type MailCountResponse struct {
	MailCount int `json:"mailCount"`
}

MailCountResponse is used to report the number of mail items in storage.

type MailItem

type MailItem struct {
	ID               string                `json:"id"`
	DateSent         string                `json:"dateSent"`
	FromAddress      string                `json:"fromAddress"`
	ToAddresses      MailAddressCollection `json:"toAddresses"`
	Subject          string                `json:"subject"`
	XMailer          string                `json:"xmailer"`
	MIMEVersion      string                `json:"mimeVersion"`
	Body             string                `json:"body"`
	ContentType      string                `json:"contentType"`
	Boundary         string                `json:"boundary"`
	Attachments      []*Attachment         `json:"attachments"`
	TransferEncoding string                `json:"transferEncoding"`

	Message           *SMTPMessagePart
	InlineAttachments []*Attachment
	TextBody          string
	HTMLBody          string
}

MailItem is a struct describing a parsed mail item. This is populated after an incoming client connection has finished sending mail data to this server.

func NewEmptyMailItem

func NewEmptyMailItem(logger *logrus.Entry) *MailItem

NewEmptyMailItem creates an empty mail object

func NewMailItem

func NewMailItem(id, dateSent string, fromAddress string, toAddresses MailAddressCollection, subject, xMailer, body, contentType, boundary string, attachments []*Attachment, logger *logrus.Entry) *MailItem

NewMailItem creates a new MailItem object

type MailSearch

type MailSearch struct {
	Message string
	Start   string
	End     string
	From    string
	To      string

	OrderByField     string
	OrderByDirection string
}

MailSearch is a set of criteria used to filter a mail collection

type MissingHeaderError

type MissingHeaderError struct {
	MissingHeader string
}

An MissingHeaderError is used to tell a caller that a requested header has not been captured or does not exist

func MissingHeader

func MissingHeader(header string) *MissingHeaderError

MissingHeader returns a new error object

func (*MissingHeaderError) Error

func (err *MissingHeaderError) Error() string

type MySQLStorage

type MySQLStorage struct {
	// contains filtered or unexported fields
}

MySQLStorage implements the IStorage interface

func NewMySQLStorage

func NewMySQLStorage(connectionInformation *ConnectionInformation, logger *logrus.Entry) *MySQLStorage

NewMySQLStorage creates a new storage object that interfaces to MySQL

func (*MySQLStorage) Connect

func (storage *MySQLStorage) Connect() error

Connect to the database

func (*MySQLStorage) Create

func (storage *MySQLStorage) Create() error

Create does nothing for MySQL

func (*MySQLStorage) DeleteMailsAfterDate

func (storage *MySQLStorage) DeleteMailsAfterDate(startDate string) (int64, error)

DeleteMailsAfterDate deletes all mails after a specified date

func (*MySQLStorage) Disconnect

func (storage *MySQLStorage) Disconnect()

Disconnect does exactly what you think it does

func (*MySQLStorage) GetAttachment

func (storage *MySQLStorage) GetAttachment(mailID, attachmentID string) (*Attachment, error)

GetAttachment retrieves an attachment for a given mail item

func (*MySQLStorage) GetMailByID

func (storage *MySQLStorage) GetMailByID(mailItemID string) (*MailItem, error)

GetMailByID retrieves a single mail item and attachment by ID

func (*MySQLStorage) GetMailCollection

func (storage *MySQLStorage) GetMailCollection(offset, length int, mailSearch *MailSearch) ([]*MailItem, error)

GetMailCollection retrieves a slice of mail items starting at offset and getting length number of records. This query is MSSQL 2005 and higher compatible.

func (*MySQLStorage) GetMailCount

func (storage *MySQLStorage) GetMailCount(mailSearch *MailSearch) (int, error)

GetMailCount returns the number of total records in the mail items table

func (*MySQLStorage) GetMailMessageRawByID

func (storage *MySQLStorage) GetMailMessageRawByID(mailItemID string) (string, error)

GetMailMessageRawByID retrieves a single mail item and attachment by ID

func (*MySQLStorage) StoreMail

func (storage *MySQLStorage) StoreMail(mailItem *MailItem) (string, error)

StoreMail writes a mail item and its attachments to the storage device. This returns the new mail ID

type NoWorkerAvailableError

type NoWorkerAvailableError struct{}

NoWorkerAvailableError is an error used when no worker is available to service a SMTP connection request.

func NoWorkerAvailable

func NoWorkerAvailable() NoWorkerAvailableError

NoWorkerAvailable returns a new instance of the No Worker Available error

func (NoWorkerAvailableError) Error

func (err NoWorkerAvailableError) Error() string

type NoopCommandExecutor

type NoopCommandExecutor struct {
	// contains filtered or unexported fields
}

NoopCommandExecutor process the command NOOP

func NewNoopCommandExecutor

func NewNoopCommandExecutor(logger *logrus.Entry, writer *SMTPWriter) *NoopCommandExecutor

NewNoopCommandExecutor creates a new struct

func (*NoopCommandExecutor) Process

func (e *NoopCommandExecutor) Process(streamInput string, mailItem *MailItem) error

Process handles the NOOP command

type Page

type Page struct {
	PublicWWWURL string
	Error        bool
	Message      string
	Theme        string
	Title        string
	User         string
}

A Page is the basis for all displayed HTML pages

type PruneCode

type PruneCode string

A PruneCode is a text code that represents a set of date ranges

func (PruneCode) ConvertToDate

func (pc PruneCode) ConvertToDate() string

ConvertToDate converts a prune code to a start date based on the current date (in UTC). This function is a bit hard-coded and weak based on the fact that the actual valid values are defined in PruneOptions. It will have to do for now.

func (PruneCode) IsValid

func (pc PruneCode) IsValid() bool

IsValid returns true/false if the prune code is a valid option.

func (PruneCode) String

func (pc PruneCode) String() string

String convers a PruneCode to string

type PruneOption

type PruneOption struct {
	PruneCode   PruneCode `json:"pruneCode"`
	Description string    `json:"description"`
}

PruneOption represents an option for pruning to be displayed in an application.

type RcptCommandExecutor

type RcptCommandExecutor struct {
	// contains filtered or unexported fields
}

RcptCommandExecutor process the RCPT TO command

func NewRcptCommandExecutor

func NewRcptCommandExecutor(
	logger *logrus.Entry,
	reader *SMTPReader,
	writer *SMTPWriter,
	emailValidationService EmailValidationProvider,
	xssService sanitizer.IXSSServiceProvider,
) *RcptCommandExecutor

NewRcptCommandExecutor creates a new struct

func (*RcptCommandExecutor) Process

func (e *RcptCommandExecutor) Process(streamInput string, mailItem *MailItem) error

Process handles the RCPT TO command. This command tells us who the recipient is

type ResetCommandExecutor

type ResetCommandExecutor struct {
	// contains filtered or unexported fields
}

ResetCommandExecutor process the command RSET

func NewResetCommandExecutor

func NewResetCommandExecutor(logger *logrus.Entry, writer *SMTPWriter) *ResetCommandExecutor

NewResetCommandExecutor creates a new struct

func (*ResetCommandExecutor) Process

func (e *ResetCommandExecutor) Process(streamInput string, mailItem *MailItem) error

Process handles the RSET command

type SMTPCommand

type SMTPCommand int

An SMTPCommand represents a command issued over a TCP connection

const (
	NONE SMTPCommand = iota
	RCPT SMTPCommand = iota
	MAIL SMTPCommand = iota
	HELO SMTPCommand = iota
	RSET SMTPCommand = iota
	DATA SMTPCommand = iota
	QUIT SMTPCommand = iota
	NOOP SMTPCommand = iota
)

Constants representing the commands that an SMTP client will send during the course of communicating with our server.

func GetCommandFromString

func GetCommandFromString(input string) (SMTPCommand, error)

GetCommandFromString takes a string and returns the integer command representation. For example if the string contains "DATA" then the value 1 (the constant DATA) will be returned.

func (SMTPCommand) String

func (smtpCommand SMTPCommand) String() string

Returns the string representation of a command.

type SMTPListener

type SMTPListener struct {
	// contains filtered or unexported fields
}

SMTPListener sets up a server that listens on a TCP socket for connections. When a connection is received a worker is created to handle processing the mail on this connection

func NewSMTPListener

func NewSMTPListener(logger *logrus.Entry, config *Configuration, mailItemChannel chan *MailItem, serverPool *ServerPool, receivers []IMailItemReceiver, connectionManager IConnectionManager) (*SMTPListener, error)

NewSMTPListener creates an SMTPListener struct

func (*SMTPListener) Dispatch

func (s *SMTPListener) Dispatch(ctx context.Context)

Dispatch starts the process of handling SMTP client connections. The first order of business is to setup a channel for writing parsed mails, in the form of MailItemStruct variables, to our database. A goroutine is setup to listen on that channel and handles storage.

Meanwhile this method will loop forever and wait for client connections (blocking). When a connection is recieved a goroutine is started to create a new MailItemStruct and parser and the parser process is started. If the parsing is successful the MailItemStruct is added to a channel. An receivers passed in will be listening on that channel and may do with the mail item as they wish.

func (*SMTPListener) Start

func (s *SMTPListener) Start() error

Start establishes a listening connection to a socket on an address.

type SMTPMessagePart

type SMTPMessagePart struct {
	Message      *mail.Message
	MessageParts []ISMTPMessagePart
	// contains filtered or unexported fields
}

An SMTPMessagePart represents a single message/content from a DATA transmission from an SMTP client. This contains the headers and body content. It also contains a reference to a collection of sub-messages, if any. This allows us to support the recursive tree-like nature of the MIME protocol.

func NewSMTPMessagePart

func NewSMTPMessagePart(logger *logrus.Entry) *SMTPMessagePart

NewSMTPMessagePart returns a new instance of this struct

func (*SMTPMessagePart) AddBody

func (messagePart *SMTPMessagePart) AddBody(body string) error

AddBody adds body content

func (*SMTPMessagePart) AddHeaders

func (messagePart *SMTPMessagePart) AddHeaders(headers textproto.MIMEHeader) error

AddHeaders takes a header set and adds it to this message part.

func (*SMTPMessagePart) BuildMessages

func (messagePart *SMTPMessagePart) BuildMessages(body string) error

BuildMessages pulls the message body from the data transmission and stores the whole body. If the message type is multipart it then attempts to parse the parts.

func (*SMTPMessagePart) ContentIsMultipart

func (messagePart *SMTPMessagePart) ContentIsMultipart() (bool, error)

ContentIsMultipart returns true if the Content-Type header contains "multipart"

func (*SMTPMessagePart) GetBody

func (messagePart *SMTPMessagePart) GetBody() string

GetBody retrieves the body portion of the message

func (*SMTPMessagePart) GetBoundary

func (messagePart *SMTPMessagePart) GetBoundary() (string, error)

GetBoundary returns the message boundary string

func (*SMTPMessagePart) GetBoundaryFromHeaderString

func (messagePart *SMTPMessagePart) GetBoundaryFromHeaderString(header string) (string, error)

GetBoundaryFromHeaderString returns the boundary marker defined in the header

func (*SMTPMessagePart) GetContentDisposition

func (messagePart *SMTPMessagePart) GetContentDisposition() string

GetContentDisposition returns the value of the Content-Disposition header

func (*SMTPMessagePart) GetContentType

func (messagePart *SMTPMessagePart) GetContentType() string

GetContentType returns the value from the Content-Type header

func (*SMTPMessagePart) GetFilenameFromContentDisposition

func (messagePart *SMTPMessagePart) GetFilenameFromContentDisposition() string

GetFilenameFromContentDisposition returns a filename from a Content-Disposition header

func (*SMTPMessagePart) GetHeader

func (messagePart *SMTPMessagePart) GetHeader(key string) string

GetHeader returns the value of a specified header key

func (*SMTPMessagePart) GetMessageParts

func (messagePart *SMTPMessagePart) GetMessageParts() []ISMTPMessagePart

GetMessageParts returns any additional sub-messages related to this message

func (*SMTPMessagePart) ParseMessages

func (messagePart *SMTPMessagePart) ParseMessages(body string, boundary string) error

ParseMessages parses messages in an SMTP body

type SMTPReader

type SMTPReader struct {
	Connection net.Conn
	// contains filtered or unexported fields
}

An SMTPReader is a simple object for reading commands and responses from a connected TCP client

func (*SMTPReader) Read

func (smtpReader *SMTPReader) Read() (string, error)

The Read function reads the raw data from the socket connection to our client. This will read on the socket until there is nothing left to read and an error is generated. This method blocks the socket for the number of milliseconds defined in CONN_TIMEOUT_MILLISECONDS. It then records what has been read in that time, then blocks again until there is nothing left on the socket to read. The final value is stored and returned as a string.

func (*SMTPReader) ReadDataBlock

func (smtpReader *SMTPReader) ReadDataBlock() (string, error)

ReadDataBlock is used by the SMTP DATA command. It will read data from the connection until the terminator is sent.

type SMTPWorker

type SMTPWorker struct {
	Connection             net.Conn
	EmailValidationService EmailValidationProvider
	Error                  error
	Reader                 *SMTPReader
	Receiver               chan *MailItem
	State                  SMTPWorkerState
	WorkerID               int
	Writer                 *SMTPWriter
	XSSService             sanitizer.IXSSServiceProvider
	// contains filtered or unexported fields
}

An SMTPWorker is responsible for executing, parsing, and processing a single TCP connection's email.

func NewSMTPWorker

func NewSMTPWorker(
	workerID int,
	pool *ServerPool,
	emailValidationService EmailValidationProvider,
	xssService sanitizer.IXSSServiceProvider,
	logger *logrus.Entry,
	logLevel string,
	logFormat string,
) *SMTPWorker

NewSMTPWorker creates a new SMTP worker. An SMTP worker is responsible for parsing and working with SMTP mail data.

func (*SMTPWorker) Prepare

func (smtpWorker *SMTPWorker) Prepare(
	connection net.Conn,
	receiver chan *MailItem,
	reader *SMTPReader,
	writer *SMTPWriter,
	killServerContext context.Context,
	connectionCloseChannel chan net.Conn,
)

Prepare tells a worker about the TCP connection they will work with, the IO handlers, and sets their state.

func (*SMTPWorker) TimeoutHasExpired

func (smtpWorker *SMTPWorker) TimeoutHasExpired(startTime time.Time) bool

TimeoutHasExpired determines if the time elapsed since a start time has exceeded the command timeout.

func (*SMTPWorker) Work

func (smtpWorker *SMTPWorker) Work()

Work is the function called by the SmtpListener when a client request is received. This will start the process by responding to the client, start processing commands, and finally close the connection.

type SMTPWorkerState

type SMTPWorkerState int

SMTPWorkerState defines states that a worker may be in. Typically a worker starts IDLE, the moves to WORKING, finally going to either DONE or ERROR.

type SMTPWriter

type SMTPWriter struct {
	Connection net.Conn
	// contains filtered or unexported fields
}

An SMTPWriter is a simple object for writing commands and responses to a client connected on a TCP socket

func (*SMTPWriter) SayGoodbye

func (smtpWriter *SMTPWriter) SayGoodbye() error

SayGoodbye tells a client that we are done communicating. This sends a 221 response. It returns true/false for success and a string with any response.

func (*SMTPWriter) SayHello

func (smtpWriter *SMTPWriter) SayHello() error

SayHello sends a hello message to a new client. The SMTP protocol dictates that you must be polite. :)

func (*SMTPWriter) SendDataResponse

func (smtpWriter *SMTPWriter) SendDataResponse() error

SendDataResponse is a function to send a DATA response message

func (*SMTPWriter) SendHELOResponse

func (smtpWriter *SMTPWriter) SendHELOResponse() error

SendHELOResponse sends a HELO message to a client

func (*SMTPWriter) SendOkResponse

func (smtpWriter *SMTPWriter) SendOkResponse() error

SendOkResponse sends an OK to a client

func (*SMTPWriter) SendResponse

func (smtpWriter *SMTPWriter) SendResponse(response string) error

SendResponse sends a response to a client connection. It returns true/false for success and a string with any response.

type SQLiteStorage

type SQLiteStorage struct {
	// contains filtered or unexported fields
}

SQLiteStorage implements the IStorage interface

func NewSQLiteStorage

func NewSQLiteStorage(connectionInformation *ConnectionInformation, logger *logrus.Entry) *SQLiteStorage

NewSQLiteStorage creates a new storage object that interfaces to SQLite

func (*SQLiteStorage) Connect

func (storage *SQLiteStorage) Connect() error

Connect to the database

func (*SQLiteStorage) Create

func (storage *SQLiteStorage) Create() error

Create creates the SQLite database file

func (*SQLiteStorage) DeleteMailsAfterDate

func (storage *SQLiteStorage) DeleteMailsAfterDate(startDate string) (int64, error)

DeleteMailsAfterDate deletes all mails after a specified date

func (*SQLiteStorage) Disconnect

func (storage *SQLiteStorage) Disconnect()

Disconnect does exactly what you think it does

func (*SQLiteStorage) GetAttachment

func (storage *SQLiteStorage) GetAttachment(mailID, attachmentID string) (*Attachment, error)

GetAttachment retrieves an attachment for a given mail item

func (*SQLiteStorage) GetMailByID

func (storage *SQLiteStorage) GetMailByID(mailItemID string) (*MailItem, error)

GetMailByID retrieves a single mail item and attachment by ID

func (*SQLiteStorage) GetMailCollection

func (storage *SQLiteStorage) GetMailCollection(offset, length int, mailSearch *MailSearch) ([]*MailItem, error)

GetMailCollection retrieves a slice of mail items starting at offset and getting length number of records. This query is MSSQL 2005 and higher compatible.

func (*SQLiteStorage) GetMailCount

func (storage *SQLiteStorage) GetMailCount(mailSearch *MailSearch) (int, error)

GetMailCount returns the number of total records in the mail items table

func (*SQLiteStorage) GetMailMessageRawByID

func (storage *SQLiteStorage) GetMailMessageRawByID(mailItemID string) (string, error)

GetMailMessageRawByID retrieves a single mail item and attachment by ID

func (*SQLiteStorage) StoreMail

func (storage *SQLiteStorage) StoreMail(mailItem *MailItem) (string, error)

StoreMail writes a mail item and its attachments to the storage device. This returns the new mail ID

type ServerPool

type ServerPool struct {
	// contains filtered or unexported fields
}

ServerPool represents a pool of SMTP workers. This will manage how many workers may respond to SMTP client requests and allocation of those workers.

func NewServerPool

func NewServerPool(logger *logrus.Entry, maxWorkers int, logLevel, logFormat string) *ServerPool

NewServerPool creates a new server pool with a maximum number of SMTP workers. An array of workers is initialized with an ID and an initial state of SMTP_WORKER_IDLE.

func (*ServerPool) JoinQueue

func (pool *ServerPool) JoinQueue(worker *SMTPWorker)

JoinQueue adds a worker to the queue.

func (*ServerPool) NextWorker

func (pool *ServerPool) NextWorker(connection net.Conn, receiver chan *MailItem, killServerContext context.Context, connectionCloseChannel chan net.Conn) (*SMTPWorker, error)

NextWorker retrieves the next available worker from the queue.

type ServiceSettings

type ServiceSettings struct {
	AuthenticationScheme string `json:"authenticationScheme"`
	URL                  string `json:"url"`
	Version              string `json:"version"`
}

ServiceSettings represents the necessary settings to connect to and talk to the MailSlurper service tier.

type Set

type Set struct {
	HeaderItems []IItem
}

A Set is a set of header items. Most emails, bodies, and attachments have more than one header to describe what the content is and how to handle it.

func NewHeaderSet

func NewHeaderSet(headers string) (*Set, error)

NewHeaderSet takes a set of headers from a raw string, creates a new Set, and returns it.

func (*Set) Get

func (set *Set) Get(headerName string) (IItem, error)

Get returns a header item by it's key name. If the key does not exist in this set a MissingHeaderError is returned.

func (*Set) ParseHeaderString

func (set *Set) ParseHeaderString(headers string) error

ParseHeaderString will take a set of headers from a raw string and parse them into a set of header items.

func (*Set) ToMap

func (set *Set) ToMap() map[string][]string

ToMap converts this structure to a map suiteable for use in a mail.Message structure

func (*Set) UnfoldHeaders

func (set *Set) UnfoldHeaders(headers string) string

UnfoldHeaders "unfolds" headers that broken up into a single line. The RFC-2822 defines "folding" as the process of breaking up large header lines into multiple lines. Long Subject lines or Content-Type lines (with boundaries) sometimes do this.

type StorageType

type StorageType int

StorageType defines types of database engines MailSlurper supports

const (
	STORAGE_MSSQL StorageType = iota
	STORAGE_SQLITE
	STORAGE_MYSQL
)

func GetDatabaseEngineFromName

func GetDatabaseEngineFromName(engineName string) (StorageType, error)

type Version

type Version struct {
	Version string `json:"version"`
}

Version is a struct that holds information about the most current version of MailSlurper

func GetServerVersionFromMaster

func GetServerVersionFromMaster() (*Version, error)

GetServerVersionFromMaster retieves the latest version information for MailSlurper from the version.json file at master in the origin repository

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL