Question

I'm working on porting some existing Python code to Go. One bit is in charge of sending emails through Exchange server (SMTP + STARTTLS). Existing (simplified) code looks like this:

import smtplib
client = smtplib.SMTP("exchangeserver.com")
client.starttls()
client.login('user', 'password')
client.sendmail('user@exchangeserver.com', 
    ['otheruser1@exchangeserver.com', 'otheruser2@exchangeserver.com'], 
    '..message..')

I'd like to do the same thing with Go, help is appreciated - thanks.

No correct solution

OTHER TIPS

Edited:

Here is an example using the tls connection setup. I don't have one to test against, so it fails on my end connecting, but I suspect this is what you need to make it work

package main

import (
  "crypto/tls"
  "fmt"
  "log"
  "net/smtp"
)

func main() {
  var (
    host     = "smtp.google.com"
    port     = 587
    from     = "foo@bar.com"
    password = "baz"
    to       = []string{"bin@bar.com"}
    msg      = []byte("This is my message")
    auth     = smtp.PlainAuth("", from, password, "smtp.gmail.com")
  )
  serverAddr := fmt.Sprintf("%s:%d", host, port)

  conn, err := tls.Dial("tcp", serverAddr, nil)
  if err != nil {
    log.Printf("Error Dialing %s\n", err)
    return
  }

  client, err := smtp.NewClient(conn, host)
  if err != nil {
    log.Printf("Error SMTP connection: %s\n", err)
    return
  }

  if ok, _ := client.Extension("AUTH"); ok {
    if err := client.Auth(auth); err != nil {
      log.Printf("Error during AUTH %s\n", err)
      return
    }
  }

  if err := client.Mail(from); err != nil {
    log.Printf("Error: %s\n", err)
    return
  }

  for _, addr := range to {
    if err := client.Rcpt(addr); err != nil {
      log.Printf("Error: %s\n", err)
      return
    }
  }

  w, err := client.Data()
  if err != nil {
    log.Printf("Error: %s\n", err)
    return
  }

  _, err = w.Write(msg)
  if err != nil {
    log.Printf("Error: %s\n", err)
    return

  }

  err = w.Close()
  if err != nil {
    log.Printf("Error: %s\n", err)
    return

  }

  client.Quit()
}

I think something like this might work. You'll have to make the appropriate changes for server, user/password, etc. Let me know if you need more help.

package main

import (
  "fmt"
  "log"
  "net/smtp"
)

func main() {
  to := "foo@foo.com"
  from := "bin@baz.com"
  password := "myPassword"
  subject := "subject line of email"
  msg := "a one-line email message"

  emailTemplate := `To: %s
Subject: %s

%s
`
  body := fmt.Sprintf(emailTemplate, to, subject, msg)
  auth := smtp.PlainAuth("", from, password, "smtp.gmail.com")
  err := smtp.SendMail(
    "smtp.gmail.com:587",
    auth,
    from,
    []string{to},
    []byte(body),
  )
  if err != nil {
    log.Fatal(err)
  }
}

To expand on Cory LaNou's answer, you can use StartTLS on a normal net.Conn. You need to define a tls.Config and then use that to upgrade the connection to use TLS.

The example below is based on Cory's answer and also steals some code from the crypto/tls test files for creating the RSA cert and private key. In production you'd obviously replace these with real certs and keys.

It may require some customization of the variables, as well as some changes to the tls.Config to suit your environment.

package main

import (
    "crypto/rsa"
    "crypto/tls"
    "encoding/hex"
    "fmt"
    "log"
    "math/big"
    "net"
    "net/smtp"
)

func main() {

    var (
        host     = "smtp.myexchange.com"
        port     = 587
        from     = "sender@example.org"
        password = "password"
        to       = []string{"recipient@example.org"}
        msg      = []byte("This is my message")
        auth     = smtp.PlainAuth("", from, password, "smtp.myexchange.com")
    )

    conf := new(tls.Config)
    conf.Certificates = make([]tls.Certificate, 1)
    conf.Certificates[0].Certificate = [][]byte{testRSACertificate}
    conf.Certificates[0].PrivateKey = testRSAPrivateKey
    conf.CipherSuites = []uint16{tls.TLS_RSA_WITH_RC4_128_SHA}
    conf.InsecureSkipVerify = true
    conf.MinVersion = tls.VersionSSL30
    conf.MaxVersion = tls.VersionTLS10
    serverAddr := fmt.Sprintf("%s:%d", host, port)

    conn, err := net.Dial("tcp", serverAddr)
    if err != nil {
        log.Printf("Error Dialing %s\n", err)
        return
    }

    client, err := smtp.NewClient(conn, host)
    if err != nil {
        log.Printf("Error SMTP connection: %s\n", err)
        return
    }

    if err = client.StartTLS(conf); err != nil {
        log.Printf("Error performing StartTLS: %s\n", err)
        return
    }

    if ok, _ := client.Extension("AUTH"); ok {
        if err := client.Auth(auth); err != nil {
            log.Printf("Error during AUTH %s\n", err)
            return
        }
    }

    if err := client.Mail(from); err != nil {
        log.Printf("Error: %s\n", err)
        return
    }

    for _, addr := range to {
        if err := client.Rcpt(addr); err != nil {
            log.Printf("Error: %s\n", err)
            return
        }
    }

    w, err := client.Data()
    if err != nil {
        log.Printf("Error: %s\n", err)
        return
    }

    _, err = w.Write(msg)
    if err != nil {
        log.Printf("Error: %s\n", err)
        return

    }

    err = w.Close()
    if err != nil {
        log.Printf("Error: %s\n", err)
        return

    }

    client.Quit()
}

// Code below from http://golang.org/src/pkg/crypto/tls/handshake_server_test.go

func bigFromString(s string) *big.Int {
    ret := new(big.Int)
    ret.SetString(s, 10)
    return ret
}

func fromHex(s string) []byte {
    b, _ := hex.DecodeString(s)
    return b
}

var testRSACertificate = fromHex("308202b030820219a00302010202090085b0bba48a7fb8ca300d06092a864886f70d01010505003045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c7464301e170d3130303432343039303933385a170d3131303432343039303933385a3045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746430819f300d06092a864886f70d010101050003818d0030818902818100bb79d6f517b5e5bf4610d0dc69bee62b07435ad0032d8a7a4385b71452e7a5654c2c78b8238cb5b482e5de1f953b7e62a52ca533d6fe125c7a56fcf506bffa587b263fb5cd04d3d0c921964ac7f4549f5abfef427100fe1899077f7e887d7df10439c4a22edb51c97ce3c04c3b326601cfafb11db8719a1ddbdb896baeda2d790203010001a381a73081a4301d0603551d0e04160414b1ade2855acfcb28db69ce2369ded3268e18883930750603551d23046e306c8014b1ade2855acfcb28db69ce2369ded3268e188839a149a4473045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746482090085b0bba48a7fb8ca300c0603551d13040530030101ff300d06092a864886f70d010105050003818100086c4524c76bb159ab0c52ccf2b014d7879d7a6475b55a9566e4c52b8eae12661feb4f38b36e60d392fdf74108b52513b1187a24fb301dbaed98b917ece7d73159db95d31d78ea50565cd5825a2d5a5f33c4b6d8c97590968c0f5298b5cd981f89205ff2a01ca31b9694dda9fd57e970e8266d71999b266e3850296c90a7bdd9")

var testRSAPrivateKey = &rsa.PrivateKey{
    PublicKey: rsa.PublicKey{
        N: bigFromString("131650079503776001033793877885499001334664249354723305978524647182322416328664556247316495448366990052837680518067798333412266673813370895702118944398081598789828837447552603077848001020611640547221687072142537202428102790818451901395596882588063427854225330436740647715202971973145151161964464812406232198521"),
        E: 65537,
    },
    D: bigFromString("29354450337804273969007277378287027274721892607543397931919078829901848876371746653677097639302788129485893852488285045793268732234230875671682624082413996177431586734171663258657462237320300610850244186316880055243099640544518318093544057213190320837094958164973959123058337475052510833916491060913053867729"),
    Primes: []*big.Int{
        bigFromString("11969277782311800166562047708379380720136961987713178380670422671426759650127150688426177829077494755200794297055316163155755835813760102405344560929062149"),
        bigFromString("10998999429884441391899182616418192492905073053684657075974935218461686523870125521822756579792315215543092255516093840728890783887287417039645833477273829"),
    },
}

Playground

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top