SMTP return different value message - go

My purpose is to send filtered values using SMTP Gmail. But I got different values from the message. Here I got character count from the message that I need.
func main() {
links := []string{"http://grafana.url.io/api/datasources/proxy/2/query?db=telegraf&q=SELECT%20last(%22used_percent%22)%20AS%20used%20FROM%20%22disk%22%20WHERE%20(%22path%22%20%3D%20'%2F')%20AND%20time%20%3E%3D%20now()%20-%2030s%20GROUP%20BY%20time(10s)%2C%20host%20ORDER%20BY%20desc&epoch=ms"}
fmt.Println("CPU with the condition over than 80%: ")
if err := checkURLS(links); err != nil {
fmt.Println("err:", err)
return
}
checklink, _ := fmt.Println(links)
subject := "TESTING SOME subject"
message := []byte("Subject:" + subject + "\n\n" +
"CPU with the condition over than 80%: \n" +
fmt.Sprint(checklink))
fmt.Println(string(message))
fmt.Println("Email Sent!")
}
Result:
Subject: TESTING SOME subject
CPU with the condition over than 25%:
28
Expected:
Subject: TESTING SOME subject
CPU with the condition over than 25%:
[{"ha-external-01":45.804}]
any suggestion? My plain full code on this https://play.golang.org/p/_fwy38hQwVv

To cite the docs:
The msg parameter should be an RFC 822-style email with headers first, a blank line, and then the message body. The lines of msg should be CRLF terminated. The msg headers should usually include fields such as "From", "To", "Subject", and "Cc". Sending "Bcc" messages is accomplished by including an email address in the to parameter but not including it in the msg headers.
The SendMail function and the net/smtp package are low-level mechanisms and provide no support for DKIM signing, MIME attachments (see the mime/multipart package), or other mail functionality. Higher-level packages exist outside of the standard library.
This part itself already mentions CRLF (\r\n) used for line termination in the messages transmitted using SMTP (and you're using sole LFs).
You could read RFC 822 (or more up-to-date versions of this original spec) to learn more on how SMTP messages are formatted, but I admit it's hard to grok.
Still, if your messages are guaranteed to be all-ASCII and have sensible line lengths, you could get away with just using proper line termination. Otherwise I'd look at github.com/go-gomail/gomail: you can copy its approach to formatting SMTP messages or use its Message type which is able to properly serialize itself to any io.Writer (which can be a *bytes.Buffer, for example).

Related

Email message with no body part when attaching a file and sending via SMTP in Golang

I am trying to send an email message with both an email body and a file attachment (a CSV file) in Go (Golang).
I am following the mime standard of a multi-part message, however I am not very familiar with the structure of the messages following that standard. I am vaguely following a Python code snippet from a colleague as a guide which is using the Python library email (I think this is from the standard library) e.g. MIMEText and MIMEMultipart.
The email message body is not showing up when executing the following Go code:
What is wrong with it?
How can I send an email message with both that file attachment and an email message body?
This function should return a byte slice to be used as a parameter for the call to smtp.SendMail from the Go standard library. See the comments below explaining what's happening to the received email message (the THIS DOES NOT SHOW UP [...] and the THIS ALSO DOES NOT SHOW UP [...]).
func msgWithAttachment(subject, filePath string) ([]byte, error) {
// this is the separator used for the various parts of the MIME message structure
// identified as "boundary"
bPlaceholder := "our-custom-separator"
// the message setup of the common/standard initial part
mime := bytes.NewBuffer(nil)
mime.WriteString(fmt.Sprintf("Subject: %s\r\nMIME-Version: 1.0\r\n", subject))
mime.WriteString(fmt.Sprintf("Content-Type: multipart/mixed; boundary=%s\r\n", bPlaceholder))
// THIS DOES NOT SHOW UP AS THE BODY OF THE EMAIL...
// mime.WriteString("\r\n")
// mime.WriteString(fmt.Sprintf("--%s\r\n", bPlaceholder))
// mime.WriteString("This should be the email message body (v1)...")
// mime.WriteString("\r\n")
// THIS ALSO DOES NOT SHOW UP AS THE BODY OF THE EMAIL...
// BUT IS NEEDED OTHERWISE THE EMAIL MESSAGE SEEMS TO CONTAIN AS ATTACHMENT THE EMAIL MESSAGE ITSELF
// (CONTAINING ITSELF THE REAL ATTACHMENT)
mime.WriteString(fmt.Sprintf("--%s\r\n", bPlaceholder))
mime.WriteString("Content-Type: text/plain; charset=utf-8\r\n")
mime.WriteString("This should be the email message body (v2)...")
// attach a file from the filesystem
_, msgFilename := filepath.Split(filePath)
mime.WriteString(fmt.Sprintf("\n--%s\r\n", bPlaceholder))
mime.WriteString("Content-Type: application/octet-stream\r\n")
mime.WriteString("Content-Description: " + msgFilename + "\r\n")
mime.WriteString("Content-Transfer-Encoding: base64\r\n")
mime.WriteString("Content-Disposition: attachment; filename=\"" + msgFilename + "\"\r\n\r\n")
fileContent, err := ioutil.ReadFile(filePath) // read and encode the content of the file
if err != nil {
return nil, err
}
b := make([]byte, base64.StdEncoding.EncodedLen(len(fileContent)))
base64.StdEncoding.Encode(b, fileContent)
mime.Write(b)
// footer of the email message
mime.WriteString("\r\n--" + bPlaceholder + "--\r\n\r\n")
return mime.Bytes(), nil
}
Coincidentally I had a similar issue the other day. I needed a blank line between the body content type and the beginning of the body itself. Below is the updated lines for this section of your code:
mime.WriteString("Content-Type: text/plain; charset=utf-8\r\n")
mime.WriteString("\r\nThis should be the email message body (v2)...")
For clarity, this newline (\r\n) doesn't have to be exactly here, it can be appended to the content-type line above. It just needs to see a blank line between the content-type and the beginning of the body.
I'm assuming the attachment is being attached without problems, correct? My assumption is this is because you have the double new-line at the end of the content-disposition line before the attachment data is added.
Reading the RFC spec helped me: https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html
Note that the encapsulation boundary must occur at the beginning of a line, i.e., following a CRLF, and that that initial CRLF is considered to be part of the encapsulation boundary rather than part of the preceding part. The boundary must be followed immediately either by another CRLF and the header fields for the next part, or by two CRLFs, in which case there are no header fields for the next part (and it is therefore assumed to be of Content-Type text/plain).

GoLang net/smtp sendMail to recipient with plus signs

I am writing a registration form using GoLang 1.5. I have it all working, but I've noticed if I attempt to sign up using an e-mail with a plus sign in it (someemail+theirlabel#theirdomain.com) I receive the following error:
Transaction failed: Illegal semicolon, not in group
Googling for this hasn't yielded much results, and it seems like Go should handle the plus sign just fine.
import (
"net/smtp"
"net/mail"
)
func sendEmail(t *Email) bool {
to := []string{"johndoe+test#gmail.com"}
err = smtp.SendMail(smtp_url, auth, from_email, to, buff.Bytes())
if err != nil {
log.Fatal(err)
// outputs: Transaction failed: Illegal semicolon, not in group
}
}
I appreciate any advice I get on this.
Thanks!
I was able to reproduce this error if and only if I had a semi-colon in the To header within the message body. E.g.:
msg := []byte("To: email1#example.com;email2#example.com\r\nSubject: foo\r\n\r\nbody\r\n")
Double-check the To header you're sending and make sure it uses commas instead of semi-colons to separate multiple recipients.

Decoding the message body from the Gmail API using Go

I'm attempting to retrieve the full message body of messages using the Gmail API in Go. Currently when I do so, I only get the first three characters of the message body which are "<ht". I'm pretty sure my issue lies with the decoding of the message body but I can't seem to figure out what I'm doing wrong.
I've looked at examples in several other languages and have tried to translate them to Go with no success. The encoded message body is rather large so I'm fairly certain some data is getting lost somewhere.
Here is an (abridged) code snippet illustrating how I've been attempting to go about this:
req := svc.Users.Messages.List("me").Q("from:someone#somedomain.com,label:inbox")
r, _ := req.Do()
for _, m := range r.Messages {
msg, _ := svc.Users.Messages.Get("me", m.Id).Format("full").Do()
for _, part := range msg.Payload.Parts {
if part.MimeType == "text/html" {
data, _ := base64.StdEncoding.DecodeString(part.Body.Data)
html := string(data)
fmt.Println(html)
}
}
}
Need to use Base64 URL encoding (slightly different alphabet than standard Base64 encoding).
Using the same base64 package, you should use:
base64.URLEncoding.DecodeString instead of
base64.StdEncoding.DecodeString.
To get URL Base64 from standard Base64, replace:
+ to - (char 62, plus to dash)
/ to _ (char 63, slash to underscore)
= to * padding
from body string (source here: Base64 decoding of MIME email not working (GMail API) and here: How to send a message successfully using the new Gmail REST API?).

Receipt address won't show up in the email sent by golang smtp client

Here is the code snippet to send the email via a local postfix server:
from := r.FormValue("from")
to := strings.Split(r.FormValue("to"), ";")
body := r.FormValue("body")
mime := "MIME-version:1.0;\nContent-Type:text/html;charset=\"UTF-8\";\n\n"
subject := fmt.Sprintf("Subject: %s\n", r.FormValue("subject"))
msg := []byte(subject + mime + body)
err := smtp.SendMail("localhost:25", nil, from, to, msg)
The email was sent/received fine. However, it is missing the receipt address in the To field of received email. I also tried it on an exchange server. The receipt addresses are missing as well. Here is what it shows in the email source.
To: Undisclosed recipients:;
Any suggestions to fix it? thanks!
You're setting the values for the mail envelope, but you haven't put any headers in the email itself except for Subject:. You should also be using \r\n as a newline for email.
A minimal example might look like:
headers := make(map[string]string)
headers["Subject"] = "this is a test"
headers["From"] = "me#example.com"
headers["To"] = "you#example.com"
body := "hello,\nthis is a test"
var msg bytes.Buffer
for k, v := range headers {
msg.WriteString(k + ": " + v + "\r\n")
}
msg.WriteString("\r\n")
msg.WriteString(body)
Some other helpful stdlib packages:
net/textproto for MIME header handling
net/mail for address handling (though the package is really only for parsing email)
http://gopkg.in/gomail.v1 for a more complete solution (there are probably many others)

How to fetch body from imap server in Go

I successfully fetched a list of email headers using the sample code from this url: https://godoc.org/code.google.com/p/go-imap/go1/imap#example-Client . However, I still haven't been able to fetch the body of the emails. Can anyone show some working sample code that could fetch the body of the emails from a imap server in Golang?
I figured out how to get the body text now.
cmd, _ = c.UIDFetch(set, "RFC822.HEADER", "RFC822.TEXT")
// Process responses while the command is running
fmt.Println("\nMost recent messages:")
for cmd.InProgress() {
// Wait for the next response (no timeout)
c.Recv(-1)
// Process command data
for _, rsp = range cmd.Data {
header := imap.AsBytes(rsp.MessageInfo().Attrs["RFC822.HEADER"])
uid := imap.AsNumber((rsp.MessageInfo().Attrs["UID"]))
body := imap.AsBytes(rsp.MessageInfo().Attrs["RFC822.TEXT"])
if msg, _ := mail.ReadMessage(bytes.NewReader(header)); msg != nil {
fmt.Println("|--", msg.Header.Get("Subject"))
fmt.Println("UID: ", uid)
fmt.Println(string(body))
}
}
cmd.Data = nil
c.Data = nil
}
The example code you've linked to demonstrates the use of the IMAP FETCH command to fetch the RFC822.HEADER message data item for a message. The RFC contains a list of standard data items you can fetch from a message.
If you want the entire mime formatted message (both headers and body), then requesting BODY should do. You can get the headers and message body separately by requesting BODY[HEADER] and BODY[TEXT] respectively. Modifying the sample program to use one of these data items should get the data you are after.

Resources