Open Source Go conference

Mar 18th - 19th 2019

Seminole Hard Rock Hotel and Casino
Hollywood, FL

Send an email in Go with smtp.SendMail

Author image

William Kennedy

I wanted to send an email from my TraceLog package when a critical exception occurred. Fortunately Go’s standard library has a package called smpt which can be found inside the net package. When you look at the documentation you are left wanting.

I spent 20 minutes researching how to use this package. After fighting through the parameters and bugs, I came up with this sample code:

package main

import (
    "bytes"
    "fmt"
    "net/smtp"
    "runtime"
    "strings"
    "text/template"
)

func main() {
    SendEmail(
        "smtp.1and1.com",
        587,
        "username@domain.com",
        "password",
        []string{"me@domain.com"},
        "testing subject",
        "<html><body>Exception 1</body></html>Exception 1")
    }

func catchPanic(err *error, functionName string) {
    if r := recover(); r != nil {
        fmt.Printf("%s : PANIC Defered : %v\n", functionName, r)

        // Capture the stack trace
        buf := make([]byte, 10000)
        runtime.Stack(buf, false)

        fmt.Printf("%s : Stack Trace : %s", functionName, string(buf))

        if err != nil {
            *err = fmt.Errorf("%v", r)
        }
    } else if err != nil && *err != nil {
        fmt.Printf("%s : ERROR : %v\n", functionName, *err)

        // Capture the stack trace
        buf := make([]byte, 10000)
        runtime.Stack(buf, false)

        fmt.Printf("%s : Stack Trace : %s", functionName, string(buf))
    }
}

func SendEmail(host string, port int, userName string, password string, to []string, subject string, message string) (err error) {
    defer catchPanic(&err, "SendEmail")

    parameters := struct {
        From string
        To string
        Subject string
        Message string
    }{
        userName,
        strings.Join([]string(to), ","),
        subject,
        message,
    }

    buffer := new(bytes.Buffer)

    template := template.Must(template.New("emailTemplate").Parse(emailScript()))
    template.Execute(buffer, &parameters)

    auth := smtp.PlainAuth("", userName, password, host)

    err = smtp.SendMail(
        fmt.Sprintf("%s:%d", host, port),
        auth,
        userName,
        to,
        buffer.Bytes())

    return err
}

func emailScript() (script string) {
    return From: {{.From}}<br /> To: {{.To}}<br /> Subject: {{.Subject}}<br /> MIME-version: 1.0<br /> Content-Type: text/html; charset=&quot;UTF-8&quot;<br /> <br /> {{.Message}}
}

The auth variable does not have to be recreated on every call. That can be created once and reused. I added my catchPanic function so you can see any exceptions that are returned while testing the code.

If you look at the raw source of the email you will see how the message parameter works:

Return-Path:
Delivery-Date: Wed, 12 Jun 2013 20:34:59 -0400
Received: from mout.perfora.net (mout.perfora.net [X.X.X.X])
    by mx.perfora.net (node=mxus0) with ESMTP (Nemesis)
    id 0MZTCn-1V3EP13 for me@domain.com; Wed, 12 Jun 2013 20:34:58 -0400
Received: from localhost (c-50-143-31-151.hsd1.fl.comcast.net [X.X.X.X])
    by mrelay.perfora.net (node=mrus4) with ESMTP (Nemesis)
    id 0Mhi4R-1V0RuN48ot-00MTc6; Wed, 12 Jun 2013 20:34:58 -0400
From: username@domain.com
To: me@domain.com
Subject: testing subject
MIME-version: 1.0
Content-Type: text/html; charset="UTF-8"
Message-Id: <0mhi4r- data-blogger-escaped-mrelay.perfora.net="">
Date: Wed, 12 Jun 2013 20:34:56 -0400
Envelope-To: me@domain.com


<html><body>Exception 1</body></html>


As always I hope this sample program saves you time and aggravation.

Go Training

We have taught Go to thousands of developers all around the world since 2014. There is no other company that has been doing it longer and our material has proven to help jump start developers 6 to 12 months ahead of their knowledge of Go. We know what knowledge developers need in order to be productive and efficient when writing software in Go.

Our classes are perfect for both experienced and beginning engineers. We start every class from the beginning and get very detailed about the internals, mechanics, specification, guidelines, best practices and design philosophies. We cover a lot about "if performance matters" with a focus on mechanical sympathy, data oriented design, decoupling and writing production software.


Capital One
Cisco
Visa
Teradata
Red Ventures

Interested in Ultimate Go Corporate Training and special pricing?

Let’s Talk Corporate Training!

Ultimate Go Programming LiveLessons

Ultimate Go Programming LiveLessons provides an intensive, comprehensive, and idiomatic view of the Go programming language. This course focuses on both the specification and implementation of the language, including topics ranging from language syntax, design, and guidelines to concurrency, testing, and profiling. This class is perfect for anyone who wants a jump-start in learning Go or wants a more thorough understanding of the language and its internals.