How to send an email with attachments in Go - email

I have found this library and have managed to send an attachment in an empty email but not to combine text and attachments.
https://github.com/sloonz/go-mime-message
How can it be done?

I ended up implementing it myself: https://github.com/scorredoira/email
Usage is very simple:
m := email.NewMessage("Hi", "this is the body")
m.From = "from#example.com"
m.To = []string{"to#example.com"}
err := m.Attach("picture.png")
if err != nil {
log.Println(err)
}
err = email.Send("smtp.gmail.com:587", smtp.PlainAuth("", "user", "password", "smtp.gmail.com"), m)

I created gomail for this purpose. It supports attachments as well as multipart emails and encoding of non-ASCII characters. It is well documented and tested.
Here is an example:
package main
func main() {
m := gomail.NewMessage()
m.SetHeader("From", "alex#example.com")
m.SetHeader("To", "bob#example.com", "cora#example.com")
m.SetAddressHeader("Cc", "dan#example.com", "Dan")
m.SetHeader("Subject", "Hello!")
m.SetBody("text/html", "Hello <b>Bob</b> and <i>Cora</i>!")
m.Attach("/home/Alex/lolcat.jpg")
d := gomail.NewPlainDialer("smtp.example.com", 587, "user", "123456")
// Send the email to Bob, Cora and Dan.
if err := d.DialAndSend(m); err != nil {
panic(err)
}
}

I prefer to use https://github.com/jordan-wright/email for email purposes.
It supports attachments.
Email for humans
The email package is designed to be simple to use, but flexible enough
so as not to be restrictive. The goal is to provide an email interface
for humans.
The email package currently supports the following:
From, To, Bcc, and Cc fields
Email addresses in both "test#example.com" and "First Last " format
Text and HTML Message Body
Attachments
Read Receipts
Custom headers
More to come!

Attachements in the SMTP protocol are sent using a Multipart MIME message.
So I suggest you simply
create a MultipartMessage
set your text in the fist part as a TextMessage (with "Content-Type", "text/plain")
add your attachements as parts using AddPart.

Related

GoLang: Send Mailjet email without Mailjet library

I am trying to send emails from my golang application using my Mailjet credentials, but I am trying to do it the normal golang way (yes, I know that their library is highly encouraged).
I have the emails working fine using the Mailjet library, but my boss made a really good point that we might not stay with Mailjet forever. If we switch to a different email solution, we don't want to have to rewrite all of our email code, we just want to change our hostname and credentials.
My printer sends emails just find through Mailjet using the same hostname and credentials, but for some reason my golang app won't!
My code was adopted from the golang smtp library SendEmail example.
Here it is (without my credentials, of course):
import (
"bytes"
"fmt"
"net/smtp"
)
func SendTestEmail() (bool, error) {
fmt.Println("Send Test Email: Enter")
success := false
hostname := "in-v3.mailjet.com"
auth := smtp.PlainAuth("", username, password, hostname)
to := []string{"me#example.com"}
msg := []byte("To: me#example.com\r\n" +
"Subject: discount Gophers!\r\n" +
"\r\n" +
"This is the email body.\r\n")
fmt.Println("Send Test Email: Sending Email")
err := smtp.SendMail(hostname+":587", auth, "sender#example.com", to, msg)
if err == nil {
fmt.Println("Send Test Email: Email successfully sent!")
success = true
} else {
fmt.Println("Send Test Email: Email failed to send", err)
}
fmt.Println("Send Test Email: Exit")
return success, err
}
Note that I am using port 587. I do not know if my printer is using 587 or 25, but it's working. I don't work when using port 25 either.
What is really weird is that smtp.SendEmail isn't returning any errors, but I still do not get any emails (yes, I am checking my junk folder)!
Also, when I log into Mailjet, I don't see that any emails were sent. I do see that an email was sent when I send something from the printer.
So, where is my email?!
Any help is greatly appreciated. Thanks!
First of all, thanks for choosing Mailjet as your email service provider! I'm leading the API Product and Developers Relations at Mailjet.
When it comes to send, you're right with SMTP. It's standard, widely supported and easy to switch (even if I don't hope we'll get there!). Our Go library will become handy when it comes to deal with our API to manage business processes.
I have several questions / feedback looking at your code:
I guess the "sender#example.com" from address used is not the one you use in your real code? Anyway, this email must have been validated on Mailjet side beforehands. See our dedicated guide
Seems you try to set some SMTP headers like Subject within the message, when it should be handled separately
Here's a working code I'm using to work with SMTP:
package main
import (
"log"
"net/smtp"
"fmt"
)
func main() {
auth := smtp.PlainAuth(
"",
"MAILJET_API_KEY",
"MAILJET_API_SECRET",
"in-v3.mailjet.com",
)
email := "foobar#test.com"
header := make(map[string]string)
header["From"] = email
header["To"] = email
header["Subject"] = "Hello Mailjet World!"
header["X-Mailjet-Campaign"] = "test"
message := ""
for k, v := range header {
message += fmt.Sprintf("%s: %s\r\n", k, v)
}
message += "\r\nHi! Thanks for using Mailjet."
err := smtp.SendMail(
"in-v3.mailjet.com:587",
auth,
email,
[]string{email},
[]byte(message),
)
if err != nil {
log.Printf("Error: %s", err)
} else {
log.Printf("Mail sent!")
}
}
Hope it helps! hAPI sending with Mailjet

How do I Insert an image into email body?

I want to send a image in email body using go lang.
Used this package from github
https://github.com/scorredoira/email
err := m.Attach("image.png")
if err1 != nil {
fmt.Println(err1)
}
Now i am able to send image file as attachment but my need is to send a image file in email body.
Thanks in advance.
You can use Gomail (I'm the author). Have a look at the Embed method which allow you to embed images in the email body:
package main
import "gopkg.in/gomail.v2"
func main() {
m := gomail.NewMessage()
m.SetHeader("From", "from#example.com")
m.SetHeader("To", "to#example.com")
m.SetHeader("Subject", "Hello!")
m.Embed("image.png")
m.SetBody("text/html", `<img src="cid:image.png" alt="My image" />`)
d := gomail.NewPlainDialer("smtp.example.com", 587, "user", "123456")
if err := d.DialAndSend(m); err != nil {
panic(err)
}
}

Parse email fields with Golang

I am trying to parse my emails in Go and I need help.
How to get access to Content-type field of mail?
cmd, _ = c.Fetch(set, "BODY[HEADER]", "BODY[1]")
for cmd.InProgress() {
for _, rsp = range cmd.Data {
header := imap.AsBytes(rsp.MessageInfo().Attrs["BODY[HEADER]"])
body := imap.AsString(rsp.MessageInfo().Attrs["BODY[1]"])
if msg, _ := mail.ReadMessage(bytes.NewReader(header)); msg != nil {
with this I can gain access to Body and Header, but when email contain included file then with BODY[1] I have all meta-data, not only pure text. To avoid that I can use BODY[1.1], but I need condition by Content-Type:[multipart/alternative] and I can't gain access ot that field.
Ok, so i figured it out by my self. But anyway, maybe someone else interested in that. You can have access to varios fields of mail by
msg.Header.Get("Content-type")
and instead of Content-type you can put any header part name.
fmt.println(msg)
to know what name fields it have

Send e-mail over smtp and change the sender's name

I am sending e-mails over smtp in golang, which works perfectly fine. To set the sender of an e-mail I use the Client.Mail funtion:
func (c *Client) Mail(from string) error
When the recipient gets the e-mail he sees the sender as plaintext e-mail address: sender#example.com
I want the sender to be displayed like: Sandy Sender <sender#example.com>.
Is this possible? I tried setting the sender to Sandy Sender <sender#example.com> or only Sandy Sender but none of them work. I get the error 501 5.1.7 Invalid address
You need to set the From field of your mail to Sandy Sender <sender#example.com>:
...
From: Sandy Sender <sender#example.com>
To: recipient#example.com
Subject: Hello!
This is the body of the message.
And use the address only (sender#example.com) in Client.Mail.
Alternatively, you can use my package Gomail:
package main
import (
"gopkg.in/gomail.v2"
)
func main() {
m := gomail.NewMessage()
m.SetAddressHeader("From", "sender#example.com", "Sandy Sender")
m.SetAddressHeader("To", "recipient#example.com")
m.SetHeader("Subject", "Hello!")
m.SetBody("text/plain", "This is the body of the message.")
d := gomail.NewPlainDialer("smtp.example.com", 587, "user", "123456")
if err := d.DialAndSend(m); err != nil {
panic(err)
}
}
You can add "From: EmailName<" + EmailAdderss + "> \r\n" to mail header to show whatever EmailName you want, and add Email Address to void duplicate mail.
You can check if a project like jpoehls/gophermail works better.
It has a test case like this one:
m.SetFrom("Domain Sender <sender#domain.com>")
It internally (main.go) calls in SetMailAddress() the method mail.ParseAddress() which is supposed to follow RFC 5322.
I think you can use mail.Address and use Address.String function formats the address
func (a *Address) String() string
String formats the address as a valid RFC 5322 address. If the address's name contains non-ASCII characters the name will be rendered according to RFC 2047.
and I write example:
go_smtp.go

WSDL/SOAP support on Go?

Are there any packages to support SOAP/WSDL on Go?
There isn't support for WSDL in Go. Support in other languages are either static or dynamic: Either structs are pre-generated from the WSDL, or it's done on the fly with hash tables.
You can, however, encode and decode SOAP requests manually. I found that the standard encoding/xml package to be insufficient for SOAP. There are so many quirks in different servers, and the limitations in encoding/xml make it difficult generate a request these servers are happy with.
For example, some servers need xsi:type="xsd:string" on every string tag. In order to do this properly your struct needs to look like this for encoding/xml:
type MethodCall struct {
One XSI
Two XSI
}
type XSI struct {
Type string `xml:"xsi:type,attr"`
Vaue string `xml:",chardata"`
}
And you construct it like this:
MethodCall{
XSI{"xsd:string", "One"},
XSI{"xsd:string", "Two"},
}
Which gives you:
<MethodCall>
<One xsi:type="xsd:string">One</One>
<Two xsi:type="xsd:string">Two</Two>
</MethodCall>
Now this might be ok. It certainly gets the job done. But what if you needed more than just a string? encoding/xml currently doesn't support interface{}.
As you can see this gets complicated. If you had one SOAP API to integrate, this probably wouldn't be too bad. What if you had several, each with their own quirks?
Wouldn't it be nice if you could just do this?
type MethodCall struct {
One string
Two string
}
Then say to encoding/xml: "This server want xsi types".
To solve this problem I created github.com/webconnex/xmlutil. It's a work in progress. It doesn't have all the features of encoding/xml's encoder/decoder, but it has what is needed for SOAP.
Here's a working example:
package main
import (
"bytes"
"encoding/xml"
"fmt"
"github.com/webconnex/xmlutil"
"log"
//"net/http"
)
type Envelope struct {
Body `xml:"soap:"`
}
type Body struct {
Msg interface{}
}
type MethodCall struct {
One string
Two string
}
type MethodCallResponse struct {
Three string
}
func main() {
x := xmlutil.NewXmlUtil()
x.RegisterNamespace("http://www.w3.org/2001/XMLSchema-instance", "xsi")
x.RegisterNamespace("http://www.w3.org/2001/XMLSchema", "xsd")
x.RegisterNamespace("http://www.w3.org/2003/05/soap-envelope", "soap")
x.RegisterTypeMore(Envelope{}, xml.Name{"http://www.w3.org/2003/05/soap-envelope", ""},
[]xml.Attr{
xml.Attr{xml.Name{"xmlns", "xsi"}, "http://www.w3.org/2001/XMLSchema-instance"},
xml.Attr{xml.Name{"xmlns", "xsd"}, "http://www.w3.org/2001/XMLSchema"},
xml.Attr{xml.Name{"xmlns", "soap"}, "http://www.w3.org/2003/05/soap-envelope"},
})
x.RegisterTypeMore("", xml.Name{}, []xml.Attr{
xml.Attr{xml.Name{"http://www.w3.org/2001/XMLSchema-instance", "type"}, "xsd:string"},
})
buf := new(bytes.Buffer)
buf.WriteString(`<?xml version="1.0" encoding="utf-8"?>`)
buf.WriteByte('\n')
enc := x.NewEncoder(buf)
env := &Envelope{Body{MethodCall{
One: "one",
Two: "two",
}}}
if err := enc.Encode(env); err != nil {
log.Fatal(err)
}
// Print request
bs := buf.Bytes()
bs = bytes.Replace(bs, []byte{'>', '<'}, []byte{'>', '\n', '<'}, -1)
fmt.Printf("%s\n\n", bs)
/*
// Send response, SOAP 1.2, fill in url, namespace, and action
var r *http.Response
if r, err = http.Post(url, "application/soap+xml; charset=utf-8; action="+namespace+"/"+action, buf); err != nil {
return
}
dec := x.NewDecoder(r.Body)
*/
// Decode response
dec := x.NewDecoder(bytes.NewBufferString(`<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope>
<soap:Body>
<MethodCallResponse>
<Three>three</Three>
</MethodCallResponse>
</soap:Body>
</soap:Envelope>`))
find := []xml.Name{
xml.Name{"", "MethodCallResponse"},
xml.Name{"http://www.w3.org/2003/05/soap-envelope", "Fault"},
}
var start *xml.StartElement
var err error
if start, err = dec.Find(find); err != nil {
log.Fatal(err)
}
if start.Name.Local == "Fault" {
log.Fatal("Fault!") // Here you can decode a Soap Fault
}
var resp MethodCallResponse
if err := dec.DecodeElement(&resp, start); err != nil {
log.Fatal(err)
}
fmt.Printf("%#v\n\n", resp)
}
With the above example I use the Find method to get the response object, or a Fault. This isn't strictly necessary. You can also do it like this:
x.RegisterType(MethodCallResponse{})
...
// Decode response
dec := x.NewDecoder(bytes.NewBufferString(`<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope>
<soap:Body>
<MethodCallResponse>
<Three>three</Three>
</MethodCallResponse>
</soap:Body>
</soap:Envelope>`))
var start *xml.StartElement
var resp Envelope
if err := dec.DecodeElement(&resp, start); err != nil {
log.Fatal(err)
}
fmt.Printf("%#v\n\n", resp)
You'll find the Find method useful when your data looks like this:
<soap:Envelope>
<soap:Body>
<MethodResponse>
<MethodResult>
<diffgr:diffgram>
<NewDataSet>
<Table1 diffgr:id="Table1" msdata:rowOrder="0" diffgr:hasChanges="inserted">
<Three>three</Three>
</Table1>
</NewDataSet>
</diffgr:diffgram>
</MethodResult>
</MethodResponse>
</soap:Body>
</soap:Envelope>
This is a DiffGram, part of Microsoft .NET. You can use the Find method to get to Table1. The Decode and DecodeElement method also works on slices. So you can pass in a []MethodCallResponse if NewDataSet happens to contain more than one result.
I do agree with Zippower that SOAP does suck. But unfortunately a lot of enterprises use SOAP, and you're sometimes forced to use these APIs. With the xmlutil package I hope to make it a little less painful to work with.
Nope.
SOAP sucks, but I had to implement a server of an already-defined protocol that uses SOAP, so I listened with net/http and decoded/encoded envelopes with encoding/xml. In few minutes, I already served my first envelope with Go.
While there's still nothing in Go itself, there is gowsdl. So far, it seems to work well enough for me to interface with several SOAP services.
I don't use the SOAP proxy it provides, which I believe doesn't support auth, but gowsdl generates the structs and code I need from the WSDL to marshal requests and unmarshal responses--a big win.
one option is to use gsoap which produces a C WSDL client
and then use that client through GO with cgo