Giter Site home page Giter Site logo

gomail's Introduction

Gomail

Build Status Code Coverage Documentation

Introduction

Gomail is a simple and efficient package to send emails. It is well tested and documented.

Gomail can only send emails using an SMTP server. But the API is flexible and it is easy to implement other methods for sending emails using a local Postfix, an API, etc.

It is versioned using gopkg.in so I promise there will never be backward incompatible changes within each version.

It requires Go 1.2 or newer. With Go 1.5, no external dependencies are used.

Features

Gomail supports:

  • Attachments
  • Embedded images
  • HTML and text templates
  • Automatic encoding of special characters
  • SSL and TLS
  • Sending multiple emails with the same SMTP connection

Documentation

https://godoc.org/gopkg.in/gomail.v2

Download

go get gopkg.in/gomail.v2

Examples

See the examples in the documentation.

FAQ

x509: certificate signed by unknown authority

If you get this error it means the certificate used by the SMTP server is not considered valid by the client running Gomail. As a quick workaround you can bypass the verification of the server's certificate chain and host name by using SetTLSConfig:

package main

import (
	"crypto/tls"

	"gopkg.in/gomail.v2"
)

func main() {
	d := gomail.NewDialer("smtp.example.com", 587, "user", "123456")
	d.TLSConfig = &tls.Config{InsecureSkipVerify: true}

    // Send emails using d.
}

Note, however, that this is insecure and should not be used in production.

Contribute

Contributions are more than welcome! See CONTRIBUTING.md for more info.

Change log

See CHANGELOG.md.

License

MIT

Contact

You can ask questions on the Gomail thread in the Go mailing-list.

gomail's People

Contributors

akavel avatar alexcesaro avatar fzerorubigd avatar jiridanek avatar lvillani avatar slavikm avatar yoshuawuyts avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gomail's Issues

Error: header.go:76: undefined: sync.Pool

Hi,

Just tried to use this package but get the following error:

$ go get gopkg.in/gomail.v1
# gopkg.in/alexcesaro/quotedprintable.v1
/home/src/gopkg.in/alexcesaro/quotedprintable.v1/header.go:76: undefined: sync.Pool
$ 

$ go version
go version go1.2.1 linux/amd64

Am I doing something wrong?

Thanks

Best regards

Mario

Auth: CRAM-MD5

Hi,

as far as I understood your code you implement the smtp.Auth interface which is then used by the smtp package when calling c.Auth().

In auth.go you implement LOGIN and PLAIN mechanisms. Have you ever thought about implementing CRAM-MD5?

You would have to return CRAM-MD5 in Auth.Send, listen for the base64 encoded challenge in Auth.Next, decode it and return (base64 encoded) the username and a MD5 response à la

digest = MD5(('password' XOR opad), MD5(('password' XOR ipad), challenge)).

Details can be found in this Tutorial and RFC 2195.

anonymous relay

Hi .

I am trying to relay on a server with no authentication .
using a telnet client , I can simply :

  1. telnet MyServer MyPort
  2. Helo
  3. MAIL FROM: [email protected]
  4. RCPT TO: [email protected]
  5. SUBJECT:how are you?
  6. period (.)

I tried to create an Auth with Start function that returns immediately , with empty return value , or "MAIL FROM:" , but I guess no matter what I do , smtp package will go through :
code, msg64, err := c.cmd(0, "AUTH %s %s", mech, resp64)
that the server will decline .

Do you have an idea for doing anonymous connection/ESMTP AUTH .
my best help so far was http://www.fehcom.de/qmail/smtpauth.html

Thanks .

No way to set more than one address header with proper encoding

With the current API, you can call SetAddressHeader once, which will pass the input through FormatAddress and set the header, or you can format multiple addresses with FormatAddress and then set the address header with a slice containing the formatted addresses.

However, the second method does not work because FormatAddress may encoding an address as needed. Then, SetHeader will encode the address again, yielding a twice-encoded final value.

Connection Reset by Peer

I have 50 worker goroutines that start with a channel passed in. Then, the process forever loops reading 100 database records at a time and sleeping for 10 seconds in between database calls. As it loops through the 100 email records to send, it passes each record through the channel to one of the 50 workers, who then sends the email. The problem is, after it goes through about 1000 emails, I start getting errors like this:

gomail: could not send email 1: read tcp 10.2.30.25:56708->216.146.40.110:25: read: connection reset by peer

I have to send out about 50k emails per day. What do you recommend? Here's the main code that processes the email queue and passes each record to the worker through the channel:

func main() {

MaxWorkers := 50
println("Creating: " + strconv.Itoa(MaxWorkers) + " workers..")
batchChannel := make(chan EmailQueue.EmailQueueObj)

for i := 0; i < MaxWorkers; i++ {
    go startWorker(batchChannel)
}

for {
    println("Getting queue..")
    data, _ := EmailQueue.GetQueue() //returns 100 database records

    println("Reading through " + strconv.Itoa(len(data)) + " records..")
    for _, element := range data {
        batchChannel <- element
    }

    time.Sleep(10 * time.Second)
}
}


func startWorker(channel chan EmailQueue.EmailQueueObj) {
var s gomail.SendCloser
var err error
open := false
for obj := range channel {
    if !open {
        s, err = dialer.Dial()
        if err != nil {
            fmt.Println(err.Error())
            return
        } else {
            sendEmail(obj, &s)
        }
    } else {
        sendEmail(obj, &s)
    }
    open = true
}
s.Close()
}

func sendEmail(obj EmailQueue.EmailQueueObj, s *gomail.SendCloser) {

m := gomail.NewMessage()
m.SetHeader("From", "[email protected]")
m.SetHeader("To", obj.Recipient)
m.SetHeader("Subject", obj.Subject.String)
m.SetBody("text/html", obj.HTML.String)

// Send the email
response := ""
status := ""
if err := gomail.Send(*s, m); err != nil {
    response = err.Error()
    status = "error"
} else {
    response = "Email sent"
    status = "sent"
}
m.Reset()

return
}

What is the correct way to make gomail work with go15vendorexperiment

Since go get does not currently support go15vendorexperiment, what is the right way to get this library? And also to import it into the project?

Inside project/src/vendor do a
git clone --depth=1 https://github.com/go-gomail/gomail github.com/go-gomail/gomail

then in code

import "github.com/go-gomail/gomail"

It seems to work for me, but just wanted to make sure.

FormatAddress shouldn't insert empty quoted name

When calling FormatAddress with an empty name, like:

FormatAddress("[email protected]", "")

It returns:

But it would be better to return:

Normally it wouldn't matter all that much, but there is a bug in Go 1.5 and up that rejects such addresses with an error: golang/go#14866

Removing the redundant "" would greatly help work around this Go bug.

In the meantime, a hack like this will work around it:

if name == "" {
    message.SetHeader("To", address) // bypass FormatAddress altogether
} else {
    message.SetAddressHeader("To", address, name)
}

Is there way to utilize same sender variable in goroutine?

Hi there,

I am trying to use following sender variable in multiple goroutine to send emails, But it does not work. (I pass same initialized sender variable in function which gets executed in goroutines pool)

It returns gomail: could not send email 1: EOF error and does not proceed

mailer := gomail.NewPlainDialer(smtpServer, smtpPort, smtpUser, smtpPassword)
sender, err := mailer.Dial()
if err != nil {
log.Println("Mailer Could not be instantiated. Error: ", err)
}

I already tried DialAndSend() in goroutines pool and it works. But I do not want to connect to server for each email as it gives multiple login attempts error from gmail server after sending more than 50 emails at same time.

Any hep would be appreciated.
Thanks

Export to []byte

It would be nice to be able to export gomail.Message to []byte. I would suggest a function Raw() []byte on Message.

I could implement this and make a pull request.

Several people on Bcc?

Hi!

Currently, I'm using msg.SetAddressHeader("Bcc", emailBcc, "bcc") to copy someone while hiding its address. Is the right way to do that? How can I copy more people?

Thanks!

go get fail

somehow running the go get gopkg.in/gomail.v1
in centos 6 is not working.. it just stuck there for very very long.
and i check the $GOPATH/src/gopkg.in folder its empty...
and i have been very curious why is it not go get github.com/go-gomail/gomail instead...

Should Message be an interface?

It seems like it might be a good idea to make Message an interface in gomail rather than a concrete struct. This would allow users of the library to use the sending functionality separately from the message creation functionality.

I'm was looking at switching over my rss mailer (https://github.com/hobeone/rss2go) to use gomail and ran into this. I could rework my mailer to use gomail.Message directly but I thought that maybe this pointed out a way to make gomail better.

Encoded headers violate RFC 2047

Here's a small Golang program which demonstrates the problem:

package main

import (
    "gopkg.in/gomail.v2"
    "os"
)

func main() {
    m := gomail.NewMessage()
    m.SetHeader("From", "[email protected]")
    m.SetHeader("To", "[email protected]")
    m.SetHeader("Subject", "{$firstname} Bienvendio a Apostólica, aquí inicia el camino de tu")
    m.SetBody("text/plain", "Hello, World")
    m.WriteTo(os.Stdout)
}

This program will output these headers:

Mime-Version: 1.0
Date: Wed, 16 Mar 2016 09:08:29 -0400
From: [email protected]
To: [email protected]
Subject: =?UTF-8?q?{$firstname}_Bienvendio_a_Apost=C3=B3lica,_aqu=C3=AD_inicia_el_camino_de_tu?=
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

Note that the Subject header line is 96 characters long. According to RFC 2047,

While there is no limit to the length of a multiple-line header
field, each line of a header field that contains one or more
'encoded-word's is limited to 76 characters.

The Subject header should have been folded at 76 characters but it was not.

If file for Embed() is missing, the error is returned but email is still sent

Hi!

This library is very helpful.
However, I encountered an inconsistent behaviour which I believe is a bug.

When a file given as an argument to Message.Embed(...) is missing on the filesystem,
there's no error returned (this method returns nothing).
Later, when calling Send(sender, message),
error is returned, but the email is sent (without embedded data of course).

This is confusing - I'd rather expect that when there's an error, an email would not be sent. When it works this way, I cannot really differentiate between such error and other error stopping message from being sent.

Per-Part Content-Transfer-Encoding?

I'd like to generate an email with a body and an attachment where each uses a different Content-Transfer-Encoding. Is this possible? It seems that each message part takes the value for the Content-Transfer-Encoding from the Message object's value for encoding, and are not independently settable.

Specifically, I want the body to have a Content-Type of "text/plain; format=flowed" with a Content-Transfer-Encoding of "8bit", and the attachment to have a Content-Type of "text/html" but with a Content-Transfer-Encoding of "quoted-printable". (I'd rather not have the format=flowed text be encoded as quoted-printable, since RFC 3676 says that it "should not" be so.)

I'm fine with manually encoding the data I give to SetBody and AddAlternative as 8bit and quoted-printable, respectively; I just need to be able set the Content-Transfer-Encoding on a per-part basis.

Is there a way to do that now? If not, does it seem like a reasonable thing to be able to do?

Thanks for any help.

FAQ example is incorrect

The example in FAQ is incorrect:

d := gomail.NewPlainDialer("smtp.example.com", "user", "123456", 587)

Should be:

d := gomail.NewPlainDialer("smtp.example.com", 587, "user", "123456")

Multiple bodies

Its often convenient to send both text/html and text/plain: pretty emails for those that support it, and basic emails for those that don't.

msg := gomail.NewMessage()

msg.SetBody("text/plain", "Hello!")
msg.SetBody("text/html", "<b>Hello!</b>")

However, the second SetBody, replaces the first.

Header Order

When constructing -> parsing -> re-constructing a message it is difficult to re-create the same message since message.WriteTo() iterates through a map and the order of headers is random. In some instances the order of the headers matters such as DKIM and Received headers. Would you be interested in a PR to support this? If so, I see it two ways:

  1. Use a slice instead of a map (like attachments) to preserve order. Unfortunately, message.SetHeaders signature will change.

  2. Change the type of header from map[string][]string to something like map[string]*hValue where hValue is something like:

type hValue struct {
    values []string
    order int
}

order increments each time a header gets added. Then message.WriteTo will sort headers by order when writing headers.

Let me know what you think, thanks.

NewDialer() should support a hostname as an argument for SMTP helo

Hi,

As you state in the comments, e-mails are sent with "localhost" as the default hostname during the SMTP helo stage.

It causes some trouble as many anti-spam solution would badly penalize any e-mail sent by a machine presenting itself as "localhost".

Please update the function so that it takes the hostname as an argument.

func NewDialer(host string, port int, username, password string) *Dialer {
        return &Dialer{
                Host:     host,
                Port:     port,
                Username: username,
                Password: password,
                SSL:      port == 465,
                LocalName: "test.domain",
        }
}

Erro TLS

undefined: tls in tls.Config

go build teste_email.go

command-line-arguments

./teste_email.go:16: undefined: tls in tls.Config

Multiple Mails over Single SMTP Connection

I wonder if it's possible to send multiple emails over one SMTP connection and if it would improve the performance? I just tested your library, it's very smooth to use but it takes about 8 seconds to send 10 mails with the same mailer object.

send email with no auth

Hi.
I need to use a relay server to send email. there is no user and no sender account.
but the library looks like its you need to auth every time.

Cannot send email from docker container

dialandsend return an error when inside a privileged docker container
When inside a kubernetes, the error does not seem to happen, but the email do not get sended.

web_1 | /api/register
web_1 | 172.18.0.1 - - [10/Apr/2016:20:51:59 +0000] "POST /api/register HTTP/1.1" 200 78
web_1 | http://localhost:9001/api/confirm/$2a$04$6Dad45u6SlTB7rWTNz2GzOc3j9jWOjT.1oTjfuvO5Bd8gKGwnvX56
web_1 | panic: x509: failed to load system roots and no roots provided
web_1 | 
web_1 | goroutine 49 [running]:
web_1 | panic(0x9f49c0, 0xe2e628)
web_1 |         /usr/lib/go/src/runtime/panic.go:464 +0x3e6
web_1 | bitbucket.org/cescoferraro/templatego/src/backend/api.SendRegisterEmail(0xc820210b60, 0x0, 0x0, 0xc8201f99d0, 0x9, 0xc8201fd540, 0x1b, 0xc820200b00, 0x3c, 0x0, ...)
web_1 |         /home/cesco/code/go/src/bitbucket.org/cescoferraro/templatego/src/backend/api/register.go:85 +0x7af
web_1 | created by bitbucket.org/cescoferraro/templatego/src/backend/api.RegisterEndPoint
web_1 |         /home/cesco/code/go/src/bitbucket.org/cescoferraro/templatego/src/backend/api/register.go:55 +0x656
templatego_web_1 exited with code 2

Crashes due to "index out of range" error.

Hi, This library crashes at writeto.go file line no: 164 with error "panic: runtime error: index out of range". I would suggest to return from 'writeStrings' functions if len(a) <= 0.

Expose Message.parts publicly?

It would be useful to have the parts exposed so we can test the body of an email (eg. is the correct activation link present in the email?).

What do you think?

Gmail SMTP: syntax error

Hey!

Could be me doing something wrong, but whenever I set the From header with FormatAddress function I get panic: 555 5.5.2 Syntax error. u8sm2660708wjq.1 - gsmtp from smtp.gmail.com

If I use the simple msg.SetHeader("From", "[email protected]") format it works.

msg := gomail.NewMessage()
// msg.SetHeader("From", e.SenderMail)) // works
msg.SetHeader("From", msg.FormatAddress(e.SenderEmail, e.SenderName)) // error
msg.SetHeader("To", e.RecieverEmail)
msg.SetHeader("Subject", e.Subject)
msg.SetBody("text/html", e.Body)
mailer := gomail.NewMailer("smtp.gmail.com", "[email protected]", "password", 587)
if err := mailer.Send(msg); err != nil {
    panic(err)
}

Move message validation out of the Mailer

Currently the a Message is only checked for validity when it is sent and all the logic for validation is in the Mailer.Send() function. It seems that this might be more useful to pull put either into it's own set of functions or as a function of the Message struct.

This would be useful when you want to check the validity of a message without sending it. - i.e. check that a message is valid and present a user of what they need to fix if it isn't.

AddAlternative bug

`
message.SetBody("text/plain", "Hello pj")

message.AddAlternative("text/html", "Hello html <b>pj</b>")

`
This produces:

`

--908b4cfdd408aaeb4ad5a5525eed978aa6361f8e322bf69397de36fa21d6
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

Hello pj
--908b4cfdd408aaeb4ad5a5525eed978aa6361f8e322bf69397de36fa21d6
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

Hello html pj
--908b4cfdd408aaeb4ad5a5525eed978aa6361f8e322bf69397de36fa21d6--
`

Hotmail shows the html version.
Gmail shows the html version.

Attachment API

I have the feeling that those signatures should have io.Reader instead of *File

func OpenFile(filename string) (*File, error)
func (msg *Message) Attach(f ...*File)
func (msg *Message) Embed(image ...*File)

And using ioutil.ReadFile (inside CreateFile()) will dump whole file contents right into memory...Which is not very good for performance and in general.

Embed/Attach files from URLs?

Hello,

I would like to send emails with embedded files from URLs.

SetCopyFunc only has access to io.Writer.

How would one replace os.Open with i.e http.Get?

Sign-in attempt prevented

Hello I am using your gomail to login to Gmail to send activation emails for my website. However I got this in my email saying:

"Sign-in attempt prevented

Hi xxxxxx,
Someone just tried to sign in to your Google Account [email protected] from an app that doesn't meet modern security standards.

"We strongly recommend that you use a secure app, like Gmail, to access your account. All apps made by Google meet these security standards. Using a less secure app, on the other hand, could leave your account vulnerable."

How do I make my signing attempt more secure? I used the standard
d := gomail.NewPlainDialer("smtp.gmail.com", 587, "[email protected]", secretpass)
to send the gmail. Any tips?

Offer way to encode headers and body separately

In some conditions, BASE64 encoding of headers is required, for example when a " character is present in a To: or From: address. However, setting BASE64 as the encoding will also cause the body to be BASE64 encoded, which will lead many spam filters to flag the message as spam. This is true at least for SpamSieve, but possibly many others.

Support socks proxy?

Hi!
My proxy server as 10.0.2.251 (squid).
Users as 10.0.0.*
If user send without proxy, connect failed. How i can use gomail with proxy? Sorry for my bad english.

Problem with Go 1.2

When I try to go get gomail, it fails due to one of the project dependency:

$ go get gopkg.in/gomail.v1 
# gopkg.in/alexcesaro/quotedprintable.v1
gopkg.in/alexcesaro/quotedprintable.v1/header.go:76: undefined: sync.Pool

I checked and it appears that sync.Pool does not exist exist in Go 1.2

My version of Go:

go version
go version go1.2.1 linux/amd64

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.