Documentation ¶
Overview ¶
Package smtp provides structures and functions to listen for and handle SMTP client connections, as well as parsing SMTP socket communications.
Index ¶
- Constants
- Variables
- func BroadcastMessageToWebsockets(message MailItemStruct)
- func ConnectMSSQL(host string, port string, database string, userName string, password string) (*sql.DB, error)
- func ConnectMySQL(host string, port string, database string, userName string, password string) (*sql.DB, error)
- func ConnectSqlite() (*sql.DB, error)
- func CreateMSSQLDatabase(db *sql.DB) error
- func CreateMySQLDatabase(db *sql.DB) error
- func CreateSqlliteDatabase(db *sql.DB) error
- func WebsocketHandler(writer http.ResponseWriter, request *http.Request)
- type Attachment
- type AttachmentHeader
- type MailBody
- type MailHeader
- type MailItemStruct
- type MailStorage
- func (ms *MailStorage) Connect() error
- func (ms *MailStorage) Disconnect()
- func (ms *MailStorage) GetAttachment(id int) map[string]string
- func (ms *MailStorage) GetMail(id int) model.JSONMailItem
- func (ms *MailStorage) GetMails() []model.JSONMailItem
- func (ms *MailStorage) StartWriteListener(dbWriteChannel chan MailItemStruct)
- type Parser
- func (parser *Parser) CommandRouter(command int, input string) bool
- func (parser *Parser) ParseCommand(line string) int
- func (parser *Parser) Process_DATA(line string) (bool, string, *MailHeader, *MailBody)
- func (parser *Parser) Process_HELO(line string) (bool, string)
- func (parser *Parser) Process_MAIL(line string) (bool, string)
- func (parser *Parser) Process_RCPT(line string) (bool, string)
- func (parser *Parser) ReadChunk() string
- func (parser *Parser) Run()
- func (parser *Parser) SendClosingResponse() (bool, string)
- func (parser *Parser) SendOkResponse() (bool, string)
- func (parser *Parser) SendResponse(resp string) (bool, string)
- type Server
- type WebsocketConnection
Constants ¶
const ( DATA int = iota RCPT int = iota MAIL int = iota HELO int = iota RSET int = iota QUIT int = iota )
Constants representing the commands that an SMTP client will send during the course of communicating with our server.
const ( STATE_START int = iota STATE_HEADER int = iota STATE_DATA_HEADER int = iota STATE_BODY int = iota STATE_QUIT int = iota STATE_ERROR int = iota )
Constants for the various states the parser can be in. The parser always starts with STATE_START and will end in either STATE_QUIT if the transmission was successful, or STATE_ERROR if something bad happened along the way.
const ( RECEIVE_BUFFER_LEN = 1024 CONN_TIMEOUT_MILLISECONDS = 5 COMMAND_TIMEOUT_SECONDS = 5 )
Constants for parser buffer sizes and timeouts. CONN_TIMEOUT_MILLISECONDS is how many milliseconds to wait before attempting to read from the socket again. COMMAND_TIMEOUT_SETTINGS is how long to hold the socket open without recieving commands before closing with an error.
const ( ENGINE_SQLITE int = 1 ENGINE_MYSQL int = 2 ENGINE_MSSQL int = 3 )
Variables ¶
var Commands = map[string]int{ "helo": HELO, "ehlo": HELO, "rcpt to": RCPT, "mail from": MAIL, "send": MAIL, "rset": RSET, "quit": QUIT, "data": DATA, }
This is a command 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.
var WebsocketConnections map[*WebsocketConnection]bool = make(map[*WebsocketConnection]bool)
Functions ¶
func BroadcastMessageToWebsockets ¶
func BroadcastMessageToWebsockets(message MailItemStruct)
This function takes a MailItemStruct and sends it to all open websockets.
func ConnectMSSQL ¶
func ConnectMySQL ¶
func ConnectSqlite ¶
func CreateMSSQLDatabase ¶
func CreateMySQLDatabase ¶
func CreateSqlliteDatabase ¶
func WebsocketHandler ¶
func WebsocketHandler(writer http.ResponseWriter, request *http.Request)
This function handles the handshake for our websocket connection. It sets up a goroutine to handle sending MailItemStructs to the other side.
Types ¶
type Attachment ¶
type Attachment struct { Headers *AttachmentHeader Contents string }
type AttachmentHeader ¶
type AttachmentHeader struct { ContentType string MIMEVersion string ContentTransferEncoding string ContentDisposition string FileName string Body string }
func (*AttachmentHeader) Parse ¶
func (this *AttachmentHeader) Parse(contents string)
Parses 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 MailBody ¶
type MailBody struct { TextBody string HTMLBody string Attachments []*Attachment }
func (*MailBody) Parse ¶
Parses a mail's DATA section. This will attempt to figure out what this mail contains. At the simples level it will contain a text message. A more complex example would be a multipart message with mixed text and HTML. It will also parse any attachments and retrieve their contents into an attachments array.
type MailHeader ¶
type MailHeader struct { ContentType string Boundary string MIMEVersion string Subject string Date string XMailer string }
func (*MailHeader) Parse ¶
func (this *MailHeader) Parse(contents string)
Given an entire mail transmission this method parses a set of mail headers. It will split 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
However some headers, such as Content-Type, may have additiona information, especially when the content type is a multipart and there are attachments. Then it can look like this:
Content-Type: multipart/mixed; boundary="==abcsdfdfd=="\r\n
type MailItemStruct ¶
type MailItemStruct struct { Id int `json:"id"` DateSent string `json:"dateSent"` FromAddress string `json:"fromAddress"` ToAddresses []string `json:"toAddresses"` Subject string `json:"subject"` XMailer string `json:"xmailer"` Body string `json:"body"` ContentType string `json:"contentType"` Boundary string `json:"boundary"` Attachments []*Attachment `json:"attachments"` }
MailItemStruct is a struct describing a parsed mail item. This is populated after an incoming client connection has finished sending mail data to this server.
type MailStorage ¶
type MailStorage struct { Engine int Host string Port string Database string UserName string Password string Db *sql.DB }
Structure for holding a persistent database connection.
var Storage MailStorage
Global variable for our server's database connection
func (*MailStorage) Connect ¶
func (ms *MailStorage) Connect() error
Open a connection to a SQLite database. This will attempt to delete any existing database file and create a new one with a blank table for holding mail data.
func (*MailStorage) Disconnect ¶
func (ms *MailStorage) Disconnect()
Close a SQLite database connection.
func (*MailStorage) GetAttachment ¶
func (ms *MailStorage) GetAttachment(id int) map[string]string
func (*MailStorage) GetMail ¶
func (ms *MailStorage) GetMail(id int) model.JSONMailItem
Retrieves a single mail item and its attachments.
func (*MailStorage) GetMails ¶
func (ms *MailStorage) GetMails() []model.JSONMailItem
Retrieves all stored mail items as an array of MailItemStruct items.
func (*MailStorage) StartWriteListener ¶
func (ms *MailStorage) StartWriteListener(dbWriteChannel chan MailItemStruct)
Listens for messages on a channel for mail messages to be written to disk. This channel takes in MailItemStruct mail items.
type Parser ¶
type Parser struct { State int Connection net.Conn MailItem MailItemStruct }
SMTP parser. The parser type keeps the current state of a parsing session, the socket connection handle, and finally collects all information into a MailItemStruct.
func (*Parser) CommandRouter ¶
This function takes a command and the raw data read from the socket connection and executes the correct handler function to process the data and potentially respond to the client to continue SMTP negotiations.
func (*Parser) ParseCommand ¶
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 (*Parser) Process_DATA ¶
Function to process 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.
- True/false for success
- Error or success message
- Headers
- Body breakdown
func (*Parser) Process_HELO ¶
Function to process the HELO and EHLO SMTP commands. This command responds to clients with a 250 greeting code and returns success or false and an error message (if any).
func (*Parser) Process_MAIL ¶
Function to process the MAIL FROM command (constant MAIL). This command will respond to clients with 250 Ok response and returns true/false for success and a string containing the sender's address.
func (*Parser) Process_RCPT ¶
Function to process the RCPT TO command (constant RCPT). This command will respond to clients with a 250 Ok response and returns true/false for success and a string containing the recipients address. Note that a client can send one or more RCPT TO commands.
func (*Parser) ReadChunk ¶
This 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 (*Parser) Run ¶
func (parser *Parser) Run()
This is the main entry function called when a new client connection is established. It begins by sending a 220 welcome message to the client to indicate we are ready to communicate. From here we initialize a parser and blank MailItemStruct to hold the data we recieve. Once we recieve the quit command we close out.
A parsing session can end with either a STATE_QUIT if all was successful, or a STATE_ERROR if there was a problem.
func (*Parser) SendClosingResponse ¶
Function to tell 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 (*Parser) SendOkResponse ¶
Function to tell a client that we recieved the last communication successfully and are ready to get our next command. This sends a 250 response. It returns true/false for success and a string with any response.
type Server ¶
Represents an SMTP server with an address and connection handle.
func (*Server) Close ¶
func (s *Server) Close()
Closes a socket connection in an Server object. Most likely used in a defer call.
Example:
smtp := Server.Server { Address: "127.0.0.1:8000" } defer smtp.Close()
func (*Server) Connect ¶
func (s *Server) Connect()
Establishes a listening connection to a socket on an address. This will set the connection handle on our Server struct.
func (*Server) ProcessRequests ¶
func (s *Server) ProcessRequests()
This function 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 SQLite 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 the database writing channel.
type WebsocketConnection ¶
type WebsocketConnection struct { // Websocket connection handle WS *websocket.Conn // Buffered channel for outbound messages SendChannel chan MailItemStruct }
Structure for tracking and working with websockets