Example #1
0
func NewMessageEncoder() *MessageEncoder {
	b := &bytes.Buffer{}
	return &MessageEncoder{
		b,
		quotedprintable.NewWriter(b),
	}
}
Example #2
0
// Write the attachment to the specified multipart writer.
func (a Attachment) Write(w *multipart.Writer) error {
	headers := make(textproto.MIMEHeader)
	if len(a.Filename) != 0 {
		headers.Add("Content-Type", fmt.Sprintf("%s; name=%s", a.ContentType, a.Filename))
	} else {
		headers.Add("Content-Type", a.ContentType)
	}
	if a.Encoded {
		headers.Add("Content-Transfer-Encoding", "base64")
	} else {
		headers.Add("Content-Transfer-Encoding", "quoted-printable")
	}
	p, err := w.CreatePart(headers)
	if err != nil {
		return err
	}
	if a.Encoded {
		if _, err := p.Write([]byte(a.Content)); err != nil {
			return err
		}
	} else {
		q := quotedprintable.NewWriter(p)
		if _, err := q.Write([]byte(a.Content)); err != nil {
			return err
		}
		return q.Close()
	}
	return nil
}
Example #3
0
// *Since the mime library in use by ```email``` is now in the stdlib, this test is deprecated
func Test_quotedPrintEncode(t *testing.T) {
	var buf bytes.Buffer
	text := []byte("Dear reader!\n\n" +
		"This is a test email to try and capture some of the corner cases that exist within\n" +
		"the quoted-printable encoding.\n" +
		"There are some wacky parts like =, and this input assumes UNIX line breaks so\r\n" +
		"it can come out a little weird.  Also, we need to support unicode so here's a fish: 🐟\n")
	expected := []byte("Dear reader!\r\n\r\n" +
		"This is a test email to try and capture some of the corner cases that exist=\r\n" +
		" within\r\n" +
		"the quoted-printable encoding.\r\n" +
		"There are some wacky parts like =3D, and this input assumes UNIX line break=\r\n" +
		"s so\r\n" +
		"it can come out a little weird.  Also, we need to support unicode so here's=\r\n" +
		" a fish: =F0=9F=90=9F\r\n")
	qp := quotedprintable.NewWriter(&buf)
	if _, err := qp.Write(text); err != nil {
		t.Fatal("quotePrintEncode: ", err)
	}
	if err := qp.Close(); err != nil {
		t.Fatal("Error closing writer", err)
	}
	if b := buf.Bytes(); !bytes.Equal(b, expected) {
		t.Errorf("quotedPrintEncode generated incorrect results: %#q != %#q", b, expected)
	}
}
Example #4
0
func ExampleNewWriter() {
	w := quotedprintable.NewWriter(os.Stdout)
	w.Write([]byte("These symbols will be escaped: = \t"))
	w.Close()

	// Output:
	// These symbols will be escaped: =3D =09
}
Example #5
0
func EncodeQuotedPrintable(value []byte) []byte {
	b := bytes.Buffer{}
	enc := MessageEncoder{
		&b,
		quotedprintable.NewWriter(&b),
	}
	enc.Write(value)
	return enc.Bytes()
}
Example #6
0
func EncodeQuotedPrintableString(value string) string {
	b := bytes.Buffer{}
	enc := MessageEncoder{
		&b,
		quotedprintable.NewWriter(&b),
	}
	enc.Write([]byte(value))
	return enc.String()
}
Example #7
0
// qpEncode uses the quoted-printable encoding to encode the provided text
func qpEncode(text []byte) []byte {
	// create buffer
	buf := new(bytes.Buffer)

	encoder := quotedprintable.NewWriter(buf)

	encoder.Write(text)
	encoder.Close()

	return buf.Bytes()
}
Example #8
0
// writeText ...
func (m *Message) writeText(w io.Writer, total int64) (int64, error) {
	written, err := io.WriteString(w, "Content-Transfer-Encoding: quoted-printable\r\n\r\n")
	total += int64(written)
	if err != nil {
		return total, err
	}
	// quotedprintable takes care of wrapping content at a good line length already
	qpWriter := quotedprintable.NewWriter(w)
	written, err = qpWriter.Write(m.Body)
	qpWriter.Close() // Must remember to close the wrapper, as it needs to flush to underlying writer
	return total + int64(written), err
}
Example #9
0
File: mail.go Project: kaey/mail
// Marshal builds a textual representation of a message with headers and quoted-printable body.
// It ignores ReturnPath, HTML and Parts.
func (m *Message) Marshal() ([]byte, error) {
	q := mime.QEncoding
	buf := new(bytes.Buffer)
	buf.WriteString(fmt.Sprintf("From: <%v>\n", m.From))
	if len(m.To) > 0 {
		buf.WriteString("To: ")
		for i, v := range m.To {
			if i != 0 {
				buf.WriteString(", ")
			}
			buf.WriteString(fmt.Sprintf("<%v>", v))
		}
		buf.WriteString("\n")
	}
	if len(m.CC) > 0 {
		buf.WriteString("CC: ")
		for i, v := range m.CC {
			if i != 0 {
				buf.WriteString(", ")
			}
			buf.WriteString(fmt.Sprintf("<%v>", v))
		}
		buf.WriteString("\n")
	}
	buf.WriteString(fmt.Sprintf("Message-ID: %v\n", m.ID))
	buf.WriteString(fmt.Sprintf("Subject: %v\n", q.Encode("utf-8", m.Subject)))
	buf.WriteString(fmt.Sprintf("Date: %v\n", m.Date.Format("Mon, 2 Jan 2006 15:04:05 -0700 (MST)")))
	for k, v := range m.Headers {
		switch k {
		case "Content-Type", "Content-Transfer-Encoding", "Content-Disposition":
			continue
		}
		buf.WriteString(fmt.Sprintf("%v: %v\n", k, q.Encode("utf-8", v)))
	}
	if m.IsHTML {
		buf.WriteString("Content-Type: text/html; charset=utf-8;\n")
	} else {
		buf.WriteString("Content-Type: text/plain; charset=utf-8;\n")
	}

	buf.WriteString("Content-Transfer-Encoding: quoted-printable\n")
	buf.WriteString("\n")

	w := quotedprintable.NewWriter(buf)
	if _, err := w.Write([]byte(m.Body)); err != nil {
		return nil, err
	}
	if err := w.Close(); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}
Example #10
0
func quotedBody(body []byte) ([]byte, error) {
	var buf bytes.Buffer
	w := quotedprintable.NewWriter(&buf)
	_, err := w.Write(body)
	if err != nil {
		return nil, err
	}

	err = w.Close()
	if err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}
Example #11
0
func main() {
	flag.Parse()

	var in io.Reader
	var out io.Writer

	if input == nil || strings.TrimSpace(*input) == "" {
		in = bufio.NewReader(os.Stdin)
	} else {
		fileBytes, err := ioutil.ReadFile(*input)
		if err != nil {
			log.Fatal(err)
		}
		in = bytes.NewReader(fileBytes)
	}

	if decode != nil && *decode == true {
		in = qp.NewReader(in)
	}

	inBytes, err := ioutil.ReadAll(in)
	if err != nil {
		log.Fatal(err)
	}

	if output == nil || strings.TrimSpace(*output) == "" {
		out = os.Stdout
	} else {
		out, err = os.OpenFile(*output, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
		if err != nil {
			log.Fatal(err)
		}
	}

	if decode == nil || *decode == false {
		out = qp.NewWriter(out)
	}

	n, err := out.Write(inBytes)
	if err != nil {
		log.Fatal(err)
	}
	if n != len(inBytes) {
		log.Fatalf("Partial write (%d != %d)\n", n, len(inBytes))
	}
}
Example #12
0
func (m *MailerImpl) generateMessage(to []string, subject, body string) ([]byte, error) {
	mid := m.csprgSource.generate()
	now := m.clk.Now().UTC()
	addrs := []string{}
	for _, a := range to {
		if !isASCII(a) {
			return nil, fmt.Errorf("Non-ASCII email address")
		}
		addrs = append(addrs, strconv.Quote(a))
	}
	headers := []string{
		fmt.Sprintf("To: %s", strings.Join(addrs, ", ")),
		fmt.Sprintf("From: %s", m.from),
		fmt.Sprintf("Subject: %s", subject),
		fmt.Sprintf("Date: %s", now.Format(time.RFC822)),
		fmt.Sprintf("Message-Id: <%s.%s.%s>", now.Format("20060102T150405"), mid.String(), m.from),
		"MIME-Version: 1.0",
		"Content-Type: text/plain; charset=UTF-8",
		"Content-Transfer-Encoding: quoted-printable",
	}
	for i := range headers[1:] {
		// strip LFs
		headers[i] = strings.Replace(headers[i], "\n", "", -1)
	}
	bodyBuf := new(bytes.Buffer)
	mimeWriter := quotedprintable.NewWriter(bodyBuf)
	_, err := mimeWriter.Write([]byte(body))
	if err != nil {
		return nil, err
	}
	err = mimeWriter.Close()
	if err != nil {
		return nil, err
	}
	return []byte(fmt.Sprintf(
		"%s\r\n\r\n%s\r\n",
		strings.Join(headers, "\r\n"),
		bodyBuf.String(),
	)), nil
}
Example #13
0
// Bytes converts the Email object to a []byte representation, including all needed MIMEHeaders, boundaries, etc.
func (e *Email) Bytes() ([]byte, error) {
	// TODO: better guess buffer size
	buff := bytes.NewBuffer(make([]byte, 0, 4096))

	headers := e.msgHeaders()
	w := multipart.NewWriter(buff)
	// TODO: determine the content type based on message/attachment mix.
	headers.Set("Content-Type", "multipart/mixed;\r\n boundary="+w.Boundary())
	headerToBytes(buff, headers)
	io.WriteString(buff, "\r\n")

	// Start the multipart/mixed part
	fmt.Fprintf(buff, "--%s\r\n", w.Boundary())
	header := textproto.MIMEHeader{}
	// Check to see if there is a Text or HTML field
	if len(e.Text) > 0 || len(e.HTML) > 0 {
		subWriter := multipart.NewWriter(buff)
		// Create the multipart alternative part
		header.Set("Content-Type", fmt.Sprintf("multipart/alternative;\r\n boundary=%s\r\n", subWriter.Boundary()))
		// Write the header
		headerToBytes(buff, header)
		// Create the body sections
		if len(e.Text) > 0 {
			header.Set("Content-Type", fmt.Sprintf("text/plain; charset=UTF-8"))
			header.Set("Content-Transfer-Encoding", "quoted-printable")
			if _, err := subWriter.CreatePart(header); err != nil {
				return nil, err
			}
			qp := quotedprintable.NewWriter(buff)
			// Write the text
			if _, err := qp.Write(e.Text); err != nil {
				return nil, err
			}
			if err := qp.Close(); err != nil {
				return nil, err
			}
		}
		if len(e.HTML) > 0 {
			header.Set("Content-Type", fmt.Sprintf("text/html; charset=UTF-8"))
			header.Set("Content-Transfer-Encoding", "quoted-printable")
			if _, err := subWriter.CreatePart(header); err != nil {
				return nil, err
			}
			qp := quotedprintable.NewWriter(buff)
			// Write the HTML
			if _, err := qp.Write(e.HTML); err != nil {
				return nil, err
			}
			if err := qp.Close(); err != nil {
				return nil, err
			}
		}
		if err := subWriter.Close(); err != nil {
			return nil, err
		}
	}
	// Create attachment part, if necessary
	for _, a := range e.Attachments {
		ap, err := w.CreatePart(a.Header)
		if err != nil {
			return nil, err
		}
		// Write the base64Wrapped content to the part
		base64Wrap(ap, a.Content)
	}
	if err := w.Close(); err != nil {
		return nil, err
	}
	return buff.Bytes(), nil
}
Example #14
0
File: email.go Project: logan/heim
func (e *Email) WriteTo(w io.Writer) (int64, error) {
	wc := &writeCounter{w: w}
	w = wc
	mpw := multipart.NewWriter(wc)

	// Write top-level headers.
	headers := e.Header
	headers.Set("Content-Type", fmt.Sprintf(`multipart/alternative; boundary="%s"`, mpw.Boundary()))
	headers.Set("MIME-Version", "1.0")
	for k, vv := range headers {
		for _, v := range vv {
			if _, err := fmt.Fprintf(w, "%s: %s\n", k, v); err != nil {
				return wc.n, err
			}
		}
	}
	if _, err := fmt.Fprintln(w); err != nil {
		return wc.n, err
	}

	// Write text part.
	textHeader := textproto.MIMEHeader{}
	textHeader.Set("Content-Type", `text/plain; charset="utf-8"; format="fixed"`)
	textHeader.Set("Content-Transfer-Encoding", "quoted-printable")
	pw, err := mpw.CreatePart(textHeader)
	if err != nil {
		return wc.n, fmt.Errorf("create text part: %s", err)
	}
	qpw := quotedprintable.NewWriter(pw)
	if _, err := qpw.Write(e.Text); err != nil {
		return wc.n, fmt.Errorf("write text part: %s", err)
	}
	if err := qpw.Close(); err != nil {
		return wc.n, fmt.Errorf("close text part: %s", err)
	}

	// Write top-level HTML multipart/related headers.

	// Chicken-and-egg: we need the inner multipart boundary to write the outer
	// part headers, but we can't get the boundary until we've constructed
	// multipart.Writer with a part writer. We'll generate the boundary ahead
	// of time and then manually supply it to the inner multipart.Writer.
	htmlRelatedBoundary := multipart.NewWriter(nil).Boundary()

	htmlRelatedHeader := textproto.MIMEHeader{}
	htmlRelatedHeader.Set("Content-Type", fmt.Sprintf(`multipart/related; boundary="%s"`, htmlRelatedBoundary))
	htmlRelatedHeader.Set("MIME-Version", "1.0")
	pw, err = mpw.CreatePart(htmlRelatedHeader)
	if err != nil {
		return wc.n, fmt.Errorf("create html multipart part: %s", err)
	}

	// Create inner multipart data for HTML with attachments.
	htmlmpw := multipart.NewWriter(pw)
	htmlmpw.SetBoundary(htmlRelatedBoundary)
	if err != nil {
		return wc.n, fmt.Errorf("set html multipart boundary: %s", err)
	}

	// Write html part.
	htmlHeader := textproto.MIMEHeader{}
	htmlHeader.Set("Content-Type", `text/html; charset="utf-8"`)
	htmlHeader.Set("Content-Transfer-Encoding", "quoted-printable")
	htmlpw, err := htmlmpw.CreatePart(htmlHeader)
	if err != nil {
		return wc.n, fmt.Errorf("create html part: %s", err)
	}
	qpw = quotedprintable.NewWriter(htmlpw)
	if _, err := qpw.Write(e.HTML); err != nil {
		return wc.n, fmt.Errorf("write html part: %s", err)
	}
	if err := qpw.Close(); err != nil {
		return wc.n, fmt.Errorf("close html part: %s", err)
	}

	// Write attachments.
	for _, att := range e.Attachments {
		attHeader := textproto.MIMEHeader{}
		attHeader.Set("Content-ID", fmt.Sprintf("<%s>", att.ContentID))
		attHeader.Set("Content-Type", mime.TypeByExtension(filepath.Ext(att.Name)))
		attHeader.Set("Content-Transfer-Encoding", "base64")
		attHeader.Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, att.Name))
		htmlpw, err := htmlmpw.CreatePart(attHeader)
		if err != nil {
			return wc.n, fmt.Errorf("create attachment %s: %s", att.Name, err)
		}
		b64w := base64.NewEncoder(base64.StdEncoding, htmlpw)
		if _, err := b64w.Write(att.Content); err != nil {
			return wc.n, fmt.Errorf("write attachment %s: %s", att.Name, err)
		}
		if err := b64w.Close(); err != nil {
			return wc.n, fmt.Errorf("close attachment %s: %s", att.Name, err)
		}
	}

	// Finalize HTML multipart/related.
	if err := htmlmpw.Close(); err != nil {
		return wc.n, fmt.Errorf("multipart close: %s", err)
	}

	// Finalize.
	if err := mpw.Close(); err != nil {
		return wc.n, fmt.Errorf("multipart close: %s", err)
	}

	return wc.n, nil
}