SMTP Content-Transfer-Encoding for html emails - email

Is there any reason why a Content-Transfer-Encoding of quoted-printable would be screwing up a link, when sent from a SMTP server?
Example:
After setting the mail from, rcpt to and entering data mode, send this (emails removed):
From: Me <me#me.com>
To: You <you#you.com>
Subject: Email Test
Mime-Version: 1.0;
Content-Type: text/html; charset="UTF-8";
Content-Transfer-Encoding: quoted-printable;
<html>
<body>
Google
</body>
</html>
Then the email source is somehow getting screwed up and the a tag is changed to Google (missing h from https).
If I change the Encoding to be 7bit then everything works fine.

For anyone that happens to come across this post - the issue was a simple one but not one that was immediately obvious to begin with.
When using quoted-printable you have to make sure you're passing properly encoded data. I was not. I was passing normal HTML through, thinking that was ok.
So in my example, the <a> tag would have to be encoded to Google then it works.
I was trying to implement this using Go, so below is a function that will implement this functionality...
// Returns a properly quoted-printable string
func toQuotedPrintable(s *string) error {
var b bytes.Buffer
w := quotedprintable.NewWriter(&b)
_, err := w.Write([]byte(*s))
if err != nil {
log.Println("Error while decoding to quoted-printable", err)
return err
}
err = w.Close()
if err != nil {
log.Println("Error while decoding to quoted-printable", err)
return err
}
*s = b.String()
return nil
}

Related

Golang: How to UTF8 both subject header and body in email?

I'm interested in how to pass string (successfully - in UTF8) containing both subject header and body of email to this function:
func sendEmail(body string) {
c, err := smtp.Dial(".....")
if err != nil {
log.Fatal(err)
}
defer c.Close()
// Set the sender and recipient.
c.Mail(".....")
c.Rcpt(".....")
// Send the email body.
wc, err := c.Data()
if err != nil {
log.Fatal(err)
}
defer wc.Close()
buf := bytes.NewBufferString(body)
if _, err = buf.WriteTo(wc); err != nil {
log.Fatal(err)
}
}
And then I've got here subject header and email body;
body := "Subject: Header string which contains ŽČĆŠĐ in name of user " + name + "!\n"
body += "Content-Type: text/html; charset=\"UTF-8\"\r\nContent-Transfer-Encoding: base64\r\n"
body += "String inside email body which also might contain ŽČĆŠĐ" + year_month_day_hour_minute + " - " + end_of_shift
//function call
sendEmail(body)
I've thought it should run ok ...and it really does display correctly subject header string (in utf8) but for some unknown reason the rest of email body gets displayed in junk form.
I've tried to change a few minor details but nothing really changed in principle.
This is my first Go example so I could easily overlook obvious.
Thanks for any thoughts on the matter!
try:
In your text subject use this function
func cSubject(subject string) string {
//return "=?iso-8859-1?Q?" + subject + "?="
return "=?utf-8?q?" + subject + "?="
}
Hi I successfully sent UTF8 email using golang with the following code
func sendContactUs(name string, email string, userInput string) {
// Sender data.
from := "some#email.address"
password := "some password"
// Receiver email address.
to := []string{
"receipient#email.address",
}
// smtp server configuration.
smtpHost := "smtp.gmail.com"
smtpPort := "587"
raw := `Subject: {name} Contact form on Web
Content-Type: text/plain; charset="UTF-8"
Dear Manager,
We receive a a form submission from webpage
name : {name}
email : {email}
message:
{message}
Kind Regards
XXXX Mailing service team.
`
raw = strings.Replace(raw, "{name}", name, -1)
raw = strings.Replace(raw, "{email}", email, -1)
raw = strings.Replace(raw, "{message}", userInput, -1)
// Message.
message := []byte(raw)
// Authentication.
auth := smtp.PlainAuth("", from, password, smtpHost)
// Sending email.
err := smtp.SendMail(smtpHost+":"+smtpPort, auth, from, to, message)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Email Sent Successfully!")
}
Please note the line with Content-type: it must start at the very beginning.
In other words, it should not have any prepending blank space.
Also, it must have a blank line after it.
It's a working code. please try it out. If you encounter any problems please let me know.

Sending email onward that was parsed using Golang net/mail.ReadMessage

I'm looking for the cleanest way in Golang to transfer a message through (i.e. act as an SMTP proxy) while performing some manipulation on the message body html (e.g. adding an open tracking pixel - not yet coded).
The net/mail package includes a method ReadMessage that parses mail headers into a map, and gives you an io.Reader for the body. This is necessary to determine the MIME parts of the body for processing, rather than just io.Copying them through. (the simple stub version of this function, shown in the block comment, does just that).
The following function copies an incoming mail "src" to an outgoing mail stream "dest". (The calling code sets these up as DotReader and DotWriter which takes care of most of the "dot" processing needed for RFC5321.
// Processing of email body via IO stream functions
package main
import (
"bufio"
"io"
"log"
"net/mail"
"strings"
)
/* If you just want to pass through the entire mail headers and body, you can just use
the following alernative:
func MailCopy(dst io.Writer, src io.Reader) (int64, error) {
return io.Copy(dst, src)
}
*/
// MailCopy transfers the mail body from downstream (client) to upstream (server)
// The writer will be closed by the parent function, no need to close it here.
func MailCopy(dst io.Writer, src io.Reader) (int64, error) {
var totalWritten int64
const smtpCRLF = "\r\n"
message, err := mail.ReadMessage(bufio.NewReader(src))
if err != nil {
return totalWritten, err
}
// Pass through headers. The m.Header map does not preserve order, but that should not matter.
for hdrType, hdrList := range message.Header {
for _, hdrVal := range hdrList {
hdrLine := hdrType + ": " + hdrVal + smtpCRLF
log.Print("\t", hdrLine)
bytesWritten, err := dst.Write([]byte(hdrLine))
totalWritten += int64(bytesWritten)
if err != nil {
return totalWritten, err
}
}
}
// Blank line denotes end of headers
bytesWritten, err := io.Copy(dst, strings.NewReader(smtpCRLF))
totalWritten += int64(bytesWritten)
if err != nil {
return totalWritten, err
}
// Copy the body
bytesWritten, err = io.Copy(dst, message.Body)
totalWritten += int64(bytesWritten)
if err != nil {
return totalWritten, err
}
return totalWritten, err
}
It does seem necessary to build this, because there is no net/mail.WriteMessage() method.
the header order is always randomised by Golang's map functionality. This seems harmless in my tests
A forced CRLF needs to be put in between the end of the headers and the body, as per RFCs. DotWriter takes care of the terminating dot.
The function shown above works, I was wondering if there is a better way to do this?

Go web service - POST tar.gz file as request body

I need to implement web service in go that processes tar.gz files and I wonder what is the correct way, what content type I need to define, etc.
plus, I found that a lot of things are handled automatically - on the client side I just post a gzip reader as request body and Accept-Encoding: gzip header is added automatically, and on the server side - I do not need to gunzip the request body, it is already extracted to tar. does that make sense?
Can I rely that it would be like this with any client?
Server:
func main() {
router := mux.NewRouter().StrictSlash(true)
router.Handle("/results", dataupload.NewUploadHandler()).Methods("POST")
log.Fatal(http.ListenAndServe(*address, router))
}
Uploader:
package dataupload
import (
"errors"
log "github.com/Sirupsen/logrus"
"io"
"net/http"
)
// UploadHandler responds to /results http request, which is the result-service rest API for uploading results
type UploadHandler struct {
uploader Uploader
}
// NewUploadHandler creates UploadHandler instance
func NewUploadHandler() *UploadHandler {
return &UploadHandler{
uploader: TarUploader{},
}
}
func (uh UploadHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
retStatus := http.StatusOK
body, err := getBody(request)
if err != nil {
retStatus = http.StatusBadRequest
log.Error("Error fetching request body. ", err)
} else {
_, err := uh.uploader.Upload(body)
}
writer.WriteHeader(retStatus)
}
func getBody(request *http.Request) (io.ReadCloser, error) {
requestBody := request.Body
if requestBody == nil {
return nil, errors.New("Empty request body")
}
var err error
// this part is commented out since somehow the body is already gunzipped - no need to extract it.
/*if strings.Contains(request.Header.Get("Accept-Encoding"), "gzip") {
requestBody, err = gzip.NewReader(requestBody)
}*/
return requestBody, err
}
Client
func main() {
f, err := os.Open("test.tar.gz")
if err != nil {
log.Fatalf("error openning file %s", err)
}
defer f.Close()
client := new(http.Client)
reader, err := gzip.NewReader(f)
if err != nil {
log.Fatalf("error gzip file %s", err)
}
request, err := http.NewRequest("POST", "http://localhost:8080/results", reader)
_, err = client.Do(request)
if err != nil {
log.Fatalf("error uploading file %s", err)
}
}
The code you've written for the client is just sending the tarfile directly because of this code:
reader, err := gzip.NewReader(f)
...
request, err := http.NewRequest("POST", "http://localhost:8080/results", reader)
If you sent the .tar.gz file content directly, then you would need to gunzip it on the server. E.g.:
request, err := http.NewRequest(..., f)
I think that's closer to the behavior you should expect third-party clients to exhibit.
Claerly not, but maybe...
Golang provides a very good support for the http client (and server). This is one of the first language to support http2 and the design of the API clearly shows their concern on having a fast http.
This is why they add Accept-Econding: gzip automatically. That will dramatically reduce the size of the server response and then optimize the transfer.
But the gzip remains an option in http 1 and not all of the client will push this header to your server.
Note that the Content-Type describes the type of data you are sending (here a tar.gz but could be application/json, test/javascript, ...), when the Accept-Encoding describes the way the data has been encoded for the transport
Go will take care of transparently handling the Accept-Encoding for you because it is responsible of the transport of the data. Then it will be up to you to handle the Content-Type because only you know how to give a sense to the content you received

Submitting form with golang http library

Oke, I'm currently trying to login in to my school website, with my own Crawler. Altough they have some protection against login. First I do a Get request to the Website so I get the token from the hidden Input field. That token I use in my next Post request to login to the url! But for some reason the http response is that I cannot resubmit the form. But with doing the same in Postman rest client (chrome plugin) I can login!
When I try to submit a form to this url:
postLoginUrl = "?username=%s&password=%s&submit=inloggen&_eventId=submit&credentialsType=ldap&lt=%s"
loginUrl = "https://login.hro.nl/v1/login"
where %s are filled in credentials
req, err := client.Post(loginUrl, "application/x-www-form-urlencoded", strings.NewReader(uri))
I'm getting as response that the Form cannot be resubmitted.
But when I try it with Postman rest client, I'm allowed to login.
code for Csrf token:
func getCSRFtoken() (key string) {
doc, err := goquery.NewDocument(loginUrl)
if err != nil {
log.Fatal(err)
}
types := doc.Find("input")
for node := range types.Nodes {
singlething := types.Eq(node)
hidden_input, _ := singlething.Attr("type")
if hidden_input == "hidden" {
key, _ := singlething.Attr("value")
return key
}
}
return ""
}
goquery.NewDocument is a http.Get()
My question now is, how does the URL get's formatted from the library
Maybe you would be better off using:
(c *Client)PostForm(url string, data url.Values) (resp *Response, err error)
from net/http like http://play.golang.org/p/8D6XI6arkz
With the params in url.Values (instead of concatenating the strings, like you are doing now.)

How to send an e-mail from Go

I'm trying to send an e-mail in Golang and I have a lot of problems with it. I'm new in Go so maybe this is very simply but I cannot find the answer on the doc.
This is what I want to do:
1. get an e-mail from the STDIN
2. parse the e-mail (getting from, to, subject, attachments and so on)
3. send this e-mail (put it again to the queue in local postfix)
I did 1 and 2 but I have a problem with 3th one.
This is what I have now:
package main
import (
"fmt"
"github.com/jhillyerd/go.enmime"
//"github.com/sendgrid/sendgrid-go"
"net/smtp"
"github.com/jordan-wright/email"
"os"
"net/mail"
"io/ioutil"
"bytes"
)
func main() {
mail_stdin, err := ioutil.ReadAll(os.Stdin)
if err != nil {
return
}
// Convert type to io.Reader
buf := bytes.NewBuffer(mail_stdin)
msg, err := mail.ReadMessage(buf)
if err != nil {
return
}
mime, err := enmime.ParseMIMEBody(msg)
if err != nil {
return
}
# saving attachments
for _, value := range mime.Attachments {
fmt.Println(value.FileName())
err := ioutil.WriteFile(value.FileName(), value.Content(), 0664)
if err != nil {
//panic(err)
return
}
fmt.Printf("From: %v\n", msg.Header.Get("From"))
fmt.Printf("Subject: %v\n", mime.GetHeader("Subject"))
fmt.Printf("Text Body: %v chars\n", len(mime.Text))
fmt.Printf("HTML Body: %v chars\n", len(mime.Html))
fmt.Printf("Inlines: %v\n", len(mime.Inlines))
fmt.Printf("Attachments: %v\n", len(mime.Attachments))
fmt.Println(mime.Attachments)
fmt.Println(mime.OtherParts)
fmt.Printf("Attachments: %v\n", mime.Attachments)
}
I already did few tests using: net/smtp, sendgrid-go and jordan-wright/email.
All I want to do is to send an e-mail (without changing anything) from the server to the queue again. Most of those modules needs to have Auth, but I just want to simply send is using sendmail, in the same way as I can do this from the bash:
# echo "test" | mail {address}
Using net/smtp you can do this fairly easily... Assuming you have an smtp server running that you can connect to without authentication. I would guess for what you're trying to accomplish it's actually a lot easier to do through something simple like your gmail ( https://www.digitalocean.com/community/tutorials/how-to-use-google-s-smtp-server )
Anyway, here's a couple code samples to cover either case;
c, err := smtp.Dial("mail.example.com:25")
if err != nil {
log.Fatal(err)
}
defer c.Close()
// Set the sender and recipient.
c.Mail("sender#example.org")
c.Rcpt("recipient#example.net")
// Send the email body.
wc, err := c.Data()
if err != nil {
log.Fatal(err)
}
defer wc.Close()
buf := bytes.NewBufferString("This is the email body.")
if _, err = buf.WriteTo(wc); err != nil {
log.Fatal(err)
}
Alternatively here's a go playground example that uses simple auth; http://play.golang.org/p/ATDCgJGKZ3 unless you've already got an smtp server running on your dev box following something like that will probably be a lot easier.