xhit / go-simple-mail Goto Github PK
View Code? Open in Web Editor NEWGolang package for send email. Support keep alive connection, TLS and SSL. Easy for bulk SMTP.
License: MIT License
Golang package for send email. Support keep alive connection, TLS and SSL. Easy for bulk SMTP.
License: MIT License
Hey,
Firstly I would like to say that this is an awesome package.
I would to request a set of methods to access the msg body for both html and alternative/text. Would this be possible?
NewSMTPClient
should be NewSMTPServer
or at least it should be documented, why this is how it is.
The example_test.go
wants to connect to localhost:25
. I'm not certain, I want to install a mail server on my machine. I definitively won't install a Go environment on my mail server.
$ go test ./...
--- FAIL: TestSendMail (0.00s)
example_test.go:50: Expected nil, got Mail Error on dailing with encryption type None: dial tcp [::1]:25: connect: connection refused connecting to client
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x63d18c]
goroutine 19 [running]:
testing.tRunner.func1.1(0x6749a0, 0x8a9cf0)
/home/dm/.local/go/src/testing/testing.go:941 +0x3d0
testing.tRunner.func1(0xc00014e240)
/home/dm/.local/go/src/testing/testing.go:944 +0x3f9
panic(0x6749a0, 0x8a9cf0)
/home/dm/.local/go/src/runtime/panic.go:967 +0x15d
github.com/xhit/go-simple-mail/v2.(*SMTPClient).Noop(...)
/home/dm/code/go-simple-mail/email.go:830
github.com/xhit/go-simple-mail/v2.TestSendMail(0xc00014e240)
/home/dm/code/go-simple-mail/example_test.go:55 +0x1bc
testing.tRunner(0xc00014e240, 0x6cc578)
/home/dm/.local/go/src/testing/testing.go:992 +0xdc
created by testing.(*T).Run
/home/dm/.local/go/src/testing/testing.go:1043 +0x357
FAIL github.com/xhit/go-simple-mail/v2 0.005s
FAIL
I'd be OK with running the tests in a confined environment, like a Docker container. Do you have something prepared for this?
In the same vein: Are there plans to automate test runs on commit, with say Github Actions?
Calendar invite attachments can be considered alternative views.
Perhaps we can add a email.AddInvite(s string)
that appends the invite to email.parts
with content type text/calendar; method=REQUEST
?
Or maybe add a content type of text/calendar
so we can use email.AddAlternative
?
Thoughts?
$ go get -u github.com/xhit/go-simple-mail/v2
cannot find package "github.com/xhit/go-simple-mail/v2" in any of:
...\github.com\xhit\go-simple-mail\v2 (from $GOROOT)
...\github.com\xhit\go-simple-mail\v2 (from $GOPATH)
I guess this is related to go modules?
Hi,
I would like to propose a small feature to the current API by providing
func (*Email) SetBodyData(contentType contentType, body []byte) *Email
to initialize body
part buffer with byte slice instead of a string.
It would help reducing the memory allocation when converting a []byte -> string -> []byte buffer.
Thanks again.
Hi! Is it thread save to invoke server.Connect on multiple "threads", or I need to create new server each time?
With simple plain strings we are unable to set the name, only the address of the recipient(s).
Use mail.Address.String() for headers, where full format works, and use mail.Address.Address for smtp's mail and rcpt methods to make it work with the smtp protocol.
Please provide a function that can get the encryption, For example: SMTP = > encryption TLS
const (
// EncryptionNone uses no encryption when sending email
EncryptionNone encryption = iota
// EncryptionSSL sets encryption type to SSL when sending email
EncryptionSSL
// EncryptionTLS sets encryption type to TLS when sending email
EncryptionTLS
)
Hey,
I'm trying to send emails from an outlook address but I get the error Mail Error: SMTP Connection timed out
after the time out.
Here's the informations provided by Microsoft (found two sources, one from Microsoft's support, the other from email's settings):
SMTP hostname: smtp-mail.outlook.com (or smtp.office365.com, tried both)
Port: 587
Encryption: STARTTLS
Here's my code:
server := mail.NewSMTPClient()
server.Host = os.Getenv("SMTP_HOST")
server.Port = 587
server.Username = os.Getenv("EMAIL")
server.Password = os.Getenv("EMAIL_PASSWORD")
server.Encryption = mail.EncryptionSTARTTLS
server.KeepAlive = false
smtpClient, err := server.Connect()
if err != nil {
return err
}
body := "<html><body><p>Hey,</p><p><a href='" +
os.Getenv("SERVER_ADDRESS") +
"/verifyEmail?code=" +
code +
"'>Click here to confirm your email</a></p><p>Cordially,</p></body></html>"
email := mail.NewMSG()
email.SetFrom("Test Email <" + os.Getenv("EMAIL") + ">").
AddTo("<a real address>").
SetSubject("E-mail verification")
email.SetBody(mail.TextHTML, body)
if email.Error != nil {
return email.Error
}
err = email.Send(smtpClient)
if err != nil {
return err
}
Thanks in advance for your help!
Hello
I cant send email, this is error code that i am getting:
Mail Error on Auth: 535 5.7.8 Error: authentication failed: UGFzc3dvcmQ6
Code:
import (
"fmt"
mail "github.com/xhit/go-simple-mail/v2"
)
func main() {
//Create the email message
server := mail.NewSMTPClient()
server.Host = "smtp.poczta.onet.pl"
server.Port = 465
server.Username = "[email protected]"
server.Password = "pass"
server.Authentication = mail.AuthLogin
server.Encryption = mail.EncryptionSSLTLS
// server.TLSConfig = &tls.Config{InsecureSkipVerify: true}
smtpClient, err := server.Connect()
if err != nil {
fmt.Println("error ")
fmt.Println(err)
return
}
fmt.Println("connected")
email := mail.NewMSG()
email.SetFrom("From aa <[email protected]>").
AddTo("").
SetSubject("New Go Email")
//Get from each mail
email.GetFrom()
email.SetBody(mail.TextPlain, "Hello Test")
//Send with high priority
email.SetPriority(mail.PriorityHigh)
// always check error after send
if email.Error != nil {
fmt.Println(email.Error)
return
}
//Pass the client to the email message to send it
fmt.Println(email.Send(smtpClient))
}
Logs:
error
Mail Error on Auth: 535 5.7.8 Error: authentication failed: UGFzc3dvcmQ6
I had never seen it. If anyone finds it,where is it?Tell me,please.
The repository contains files both CRLF
and LF
file endings.
Examples, not a complete list:
.gitattributes
, smtp_test.go
, LICENSE
go.mod
, smtp.go
i need get full msg for append to draft mailbox.
When SendTimeout
is triggered, send()
will result in a race since client
is used in 2 different goroutines at the same time. This is happening because textproto.Conn
isn't thread-safe, and we are calling .quit()
and .Close()
at the same time.
I am including test code here. Save it as a test file inside the same package and run it with go test -race -test.run=TestSendRace
.
package mail
import (
"fmt"
"log"
"net"
"testing"
"time"
)
func TestSendRace(t *testing.T) {
port := 56666
timeout := 1 * time.Second
startService(port, []string{
`220 test connected`,
`250 after helo`,
`250 after mail from`,
`250 after rcpt to`,
`354 after data`,
})
server := NewSMTPClient()
server.ConnectTimeout = timeout
server.SendTimeout = timeout
server.KeepAlive = false
server.Host = `localhost`
server.Port = port
smtpClient, err := server.Connect()
if err != nil {
log.Fatalf("couldn't connect: %s", err.Error())
}
defer smtpClient.Close()
err = SendMessage(`foo@bar`, []string{`rcpt@bar`}, "body", smtpClient)
if err != nil {
log.Fatalf("couldn't send: %s", err.Error())
}
}
func startService(port int, responses []string) {
log.Printf("starting service at %d...\n", port)
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
if err != nil {
log.Fatalf("couldn't listen to port %d: %s", port, err)
}
go func() {
for {
conn, err := listener.Accept()
if err != nil {
log.Fatalf("couldn't listen accept the request in port %d", port)
}
go respond(conn, responses)
}
}()
}
func respond(conn net.Conn, responses []string) {
buf := make([]byte, 1024)
for i, resp := range responses {
conn.Write([]byte(resp + "\n"))
n, err := conn.Read(buf)
if err != nil {
log.Println("couldn't read data")
return
}
readStr := string(buf[:n])
log.Printf("READ:%s", string(readStr))
if i == len(responses)-1 {
break
}
}
time.Sleep(5 * time.Second)
conn.Write([]byte(`221 after quit` + "\n"))
conn.Close()
}
Hi,
Mails not receiving to yahoo accounts. Getting the error message 5.6.7(destination does not support SMTPUTF8 required for message delivery).
Please give a soulution
Hi there,
Wireguard ui is using this program for sending mail...
I found that mail server settings are stored in /etc/envirionment but it's not working
hostname=smtp.gmail.com
port=587
username=[email protected]
password=vlqsolltfknmwfcd #generated app passwd
authType=PLAIN
encryption=TLS
noTLSCeck=no
fromName="This Email"
from="[email protected]"
I have installed ssmtp and this can send mail..
I got this message in mailtrap.io "Spam check":
MISSING_MIMEOLE Message has X-MSMail-Priority, but no X-MimeOLE
Is all good with this header?
SpamAssassin Rule: MISSING_MIMEOLE Standard description: Message has X-MSMail-Priority, but no X-MimeOLE Explanation The message is pretending to be generated by a Microsoft email program which uses the extension header X-MSMail-Priority, but is missing the extension header X-MimeOLE which is characteristic of Microsoft email. This suggests that the sender is using badly-written mailout software, rather than a genuine Microsoft email program.
Hello there,
is there any interest or maybe already any ongoing development (though i did not see anything in the branches) regarding a generic OAuth2.0 implementation?
Microsoft seems to force everyone from BasicAuthentication to OAuth2.0
I am a big fan of this package due to its simplicity and i was wondering if anyone else stumbled across this?
I noticed you are aware that OAuth2 is not working in this issue
Are you interested in adding OAuth2 as this will be the next Generation of Authentications?
I probably could help implementing it, if you are interested.
Best regards,
LarsTi
The package return error when send only to Cc or Bcc. Requires the To header.
Sending a mail from gmail or outlook with only specified Cc or Bcc works.
Hi! Fix this please!
SetListUnsubscribe("<mailto:[email protected]?subject=https://example.com/unsubscribe>").
Hi
Thanks for creating this package, great work 👍
I wanted to know if is there any specific reason why you are not adding a duplicate email address in recipients and setting error on email also?
email.recipients, err = addAddress(email.recipients, address.Address)
if err != nil {
email.Error = errors.New("Mail Error: " + err.Error() + "; Header: [" + header + "] Address: [" + addresses[i] + "]")
return email
There are SMTP servers that require not authentication.
Hey, first things first: nice package, thank you for that. But it seems that it is not really possible to keep one connection open?
I want to run this in the background and from time to time send a mail. As far as I understood the documentation (or sadly/more precisely the code 😬) right, it is not intended to reuse a client? Instead I should create a new client for each message (or hope that the connection is still open/use client.noop and maybe re-create the client)? This seems a bit wasteful to open a new connection for every message.
Maybe this could be an improvement for v3? 😁
Does the sending limit depend on the host used?
the email service which is used by me, has the Passwords need to be updated regularly mechanism.
so the account will be locked can not login when It's been a long time since last login.
in this case, the library give error info like belong
Mail Error: SMTP Connection timed out
Mail Error: No SMTP Client Provided
In this case, can you give a password related error message: The account password is correct, but the password needs to be updated, so you cannot log in immediately. You need to change the password and log in again.
Is it possible to use a SMTP connection uri instead of specify each param?
Example:
smtps://user:[email protected]:993/
Only from my perspective.
The library is very good. I'm going to use it. But there are several questions. Why is the NewClient function used to create a server?
server := mail.NewSMTPClient()
Don't you think this is illogical? Or am I missing something?
I ended up spending several hours trying to make a type that would wrap the SMTPClient
type and do the NOOP sending to implement the suggestion in this comment before I realized there was no easy way to track the last time an email was sent for the timer, and you cannot do that by wrapping a SMTPClient
because the email type is what sends the email in Email.Send
, by calling an an unexported internal function. This means that I need to create a special EmailProvider
which returns a new type embedding Email
so that I can wrap Email.Send
and Email.SendEnvelopeFrom
to track the SMTPClient
with a mutex and reset the loop. Also SMTPClient
has an exported Client
field, which returns the unexported internal smtpClient
type, which is unfortunately useless. So I cannot have the SMTPServer
reconnect the internal smtpClient
, and I must use EmailProvider
to do that. It's a bit convoluted and not nearly as easy as the solution in #23 would suggest.
It would be nice if there was a way to do this in the library, or if you could provide a type like my EmailProvider
that wraps the client and server objects to re-create it when necessary.
Currently we have the following function signatures for adding attachments to an Email struct:
func (email *Email) AddAttachment(file string, name ...string) *Email
func (email *Email) AddAttachmentBase64(b64File, name string) *Email
func (email *Email) AddAttachmentData(data []byte, filename, mimeType string) *Email
func (email *Email) AddInline(file string, name ...string) *Email
func (email *Email) AddInlineBase64(b64File, name, mimeType string) *Email
func (email *Email) AddInlineData(data []byte, filename, mimeType string) *Email
For some reason AddAttachment
and AddInline
are variadic but only accept one name:
if len(name) > 1 {
email.Error = errors.New("Mail Error: Attach can only have a file and an optional name")
return email
}
Other functions take name
as parameter and possibly also mimeType
.
I propose we modify all these functions to accept name
and mimeType
. So the API would become:
func (email *Email) AddAttachment(file, name, mimeType string) *Email
func (email *Email) AddAttachmentBase64(b64Data, name, mimeType string) *Email
func (email *Email) AddAttachmentData(data []byte, name, mimeType string) *Email
func (email *Email) AddInline(file, name, mimeType string) *Email
func (email *Email) AddInlineBase64(b64Data, name, mimeType string) *Email
func (email *Email) AddInlineData(data []byte, name, mimeType string) *Email
edit. I changed argument name b64File
-> b64Data
so it is consistent with file
and data
arguments.
Before that I used Linux, when I switched to Windows, I started to get an error when starting the program:
Mail Error on dialing with encryption type STARTTLS: dial tcp 127.0.0.1:587: connectex: No connection could be made because the target machine actively refused it.
Hi @xhit,
thanks for your work on this! Just a quick question regarding the priority type. Is there any reason why it's not exported? It being an unexported type makes it kinda annoying to translate priorities between this and other packages.
If there's a specific reason, please let me know. If not, I got a PR basically ready!
Cheers
After a bit of research, I found out that this project is capable of using a calendar in the body (#50). Can someone give me an example of how to create the ics data that is acceptable to be used? I don't see any example on the PR that has been merged.
Can you make connecting to the server and sending messages using context?
D:\Test\Tets>go get github.com/xhit/go-simple-mail/v2
cannot find package "github.com/xhit/go-simple-mail/v2" in any of:
c:\go\src\github.com\xhit\go-simple-mail\v2 (from $GOROOT)
C:\Users\Code\go\src\github.com\xhit\go-simple-mail\v2 (from $GOPATH)
First of all, thank you for this great library.
If you can make mail.contentType enum public, we can hold plain text or html formats in enum instead of integer values.
Right now, we do:
// Check Content Type
if message.ContentType == 1 {
// Set Body HTML String
email.SetBody(mail.TextHTML, message.Body)
} else {
// Set Body Plain String
email.SetBody(mail.TextPlain, message.Body)
}
What I want to do, is setting message.ContentType as mail.ContentType, so I can use directly:
email.SetBody(message.ContentType, message.Body)
If you accept this suggestion, I can prepare a PR.
Thanks.
Hello @xhit
First, thanks for the package !
Would it make sense to make the type type authType
public like what you have done for Encryption
?
So in a method which creates the SMTP client could take the authType as string
then cast it to authType.
Cheers.
Hello, I hope you are happy using this package :)
Currently I'm too busy, and I know this package needs some features proposed in this discussion #53
I left a branch here for V3 https://github.com/xhit/go-simple-mail/tree/v3, feel free to add some features, breaking changes, enhancement, etc... that you think is needed, I will check all PRs.
The PR destination should be v3 branch.
Also, If you want to be a maintainer, please respond this issue :)
i do some test with keepalive,but got some wrong (smtp server with mailhog )
2020/11/22 01:42:01 [SMTP 172.21.0.1:40294] [PROTO: RCPT] In RCPT state
2020/11/22 01:42:01 [SMTP 172.21.0.1:40294] [PROTO: RCPT] Got unknown command for RCPT state: '&{MAIL FROM:<[email protected]> MAIL FROM:<[email protected]>}'
2020/11/22 01:42:01 [SMTP 172.21.0.1:40294] Sent 26 bytes: '500 Unrecognised command\r\n'
2020/11/22 01:42:01 [SMTP 172.21.0.1:40294] Received 31 bytes: 'RCPT TO:<[email protected]>\r\nRSET\r\n'
some links https://github.com/rongfengliang/golang-email-learning/blob/master/main.go#L15
I saw question and comment about proxy in closed issue.
However it's not a network issue.
With "net/smtp" and "golang.org/x/net/proxy" we can easily create smtpClient through proxy:
Dial, err := proxy.SOCKS5("tcp", "some-proxy-address", nil, proxy.Direct)
if err != nil {
return nil, errors.Wrap(err, "Create SOCKS5")
}
dialer, err = Dial.Dial("tcp", "host-addr")
if err != nil {
return nil, errors.Wrap(err, "Dial")
}
conf := &tls.Config{ServerName: "host-addr-without-port"}
tlsdialer := tls.Client(dialer, conf)
client, err := smtp.NewClient(tlsdialer, "host-addr")
if err != nil {
panic(err)
}
auth := smtp.PlainAuth("", user, pass, "host-addr")
if err = client.Auth(auth); err != nil {
panic(err)
}
And would be awesome if smth like this would be possible in this package. Cuz it's quite convient for sending mail.
Or if it's too much trouble, then why was smtpClient made not exportable in the first place. It has couple slight differences with net's smtp.Client type: there's a instead of auth and Text made exportable.
If instead of creating separate type net's smtp.Client was used then we would have proxy as in code above.
Even if there's a different type, then methods to change connections would work too.
Or add couple params to SMTPServer type for proxy and on connect if there are filled in use them for connection.
So many ways to do it. Wander why it wasn't done in the first place. (
If ConnectTimeout
is 0 (means no timeout) and credentials are not correct (incorrect username or password or both) then the returned SMTPClient
is nil and error too.
The error Mail Error: No SMTP Client Provided
is displayed instead and authentication error.
Hello there.
Geting
Must issue a STARTTLS command first.
Error when trying to use smtp.gmail.com as host.
Is it possible to use use gmail host? I was trying other go-mail package with Google App password and everything worked
Here is the code:
server := mail.NewSMTPClient()
server.Port = 587
server.Username = "<my gmail>"
server.Password = "<App password for this gmail>"
server.Host = "smtp.gmail.com"
client, err := server.Connect()
if err != nil {
return err
}
server.Encryption = mail.EncryptionSTARTTLS
server.TLSConfig = &tls.Config{InsecureSkipVerify: true}
//Tried without those options and got the same error
email := mail.NewMSG()
email.SetFrom("<my gmail>")
email.AddTo("<my other gmail>")
email.SetBody(mail.TextPlain, "this is a test")
if email.Error != nil{
log.Fatal(email.Error)
}
err = email.Send(client)
if err != nil {
log.Println(err)
} else {
log.Println("Email Sent")
}
return nil
}
Am I missing something?
Thanks in advance
I had a smtp connexion OK (with keepalive), when suddently the remote server closed it.
Since this event no mail were sent, event if the remote server is back online.
Here is the log i have each time i send a mail since the event.
write tcp 127.0.0.1:60591->127.0.0.1:25: wsasend: An existing connection was forcibly closed by the remote host.
The only way to get my mails sent again, was by killing/starting my app.
You can easily reproduce this case with mailHog, by activating "JIM" with -invite-jim -jim-disconnect "1"
Hi, I want to know that have anyway to get mail ID when send by SMTP with this lib?
Many clients simply error out on gmail due to "Less Secure Access"; Does this library have a fix for that?
please add example as well.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.