Пример #1
0
func writeJSONPart(writer *multipart.Writer, contentType string, body Body, compressed bool) (err error) {
	bytes, err := json.Marshal(body)
	if err != nil {
		return err
	}
	if len(bytes) < kMinCompressedJSONSize {
		compressed = false
	}

	partHeaders := textproto.MIMEHeader{}
	partHeaders.Set("Content-Type", contentType)
	if compressed {
		partHeaders.Set("Content-Encoding", "gzip")
	}
	part, err := writer.CreatePart(partHeaders)
	if err != nil {
		return err
	}

	if compressed {
		gz := gzip.NewWriter(part)
		_, err = gz.Write(bytes)
		gz.Close()
	} else {
		_, err = part.Write(bytes)
	}
	return
}
Пример #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
}
Пример #3
0
// Adds a new part to the given multipart writer, containing the given revision.
// The revision will be written as a nested multipart body if it has attachments.
func (db *Database) WriteRevisionAsPart(revBody Body, isError bool, compressPart bool, writer *multipart.Writer) error {
	partHeaders := textproto.MIMEHeader{}
	docID, _ := revBody["_id"].(string)
	revID, _ := revBody["_rev"].(string)
	if len(docID) > 0 {
		partHeaders.Set("X-Doc-ID", docID)
		partHeaders.Set("X-Rev-ID", revID)
	}

	if hasInlineAttachments(revBody) {
		// Write as multipart, including attachments:
		// OPT: Find a way to do this w/o having to buffer the MIME body in memory!
		var buffer bytes.Buffer
		docWriter := multipart.NewWriter(&buffer)
		contentType := fmt.Sprintf("multipart/related; boundary=%q",
			docWriter.Boundary())
		partHeaders.Set("Content-Type", contentType)
		db.WriteMultipartDocument(revBody, docWriter, compressPart)
		docWriter.Close()
		content := bytes.TrimRight(buffer.Bytes(), "\r\n")

		part, err := writer.CreatePart(partHeaders)
		if err == nil {
			_, err = part.Write(content)
		}
		return err
	} else {
		// Write as JSON:
		contentType := "application/json"
		if isError {
			contentType += `; error="true"`
		}
		return writeJSONPart(writer, contentType, revBody, compressPart)
	}
}
Пример #4
0
// Writes a revision to a MIME multipart writer, encoding large attachments as separate parts.
func (db *Database) WriteMultipartDocument(body Body, writer *multipart.Writer) {
	// First extract the attachments that should follow:
	following := []attInfo{}
	for name, value := range BodyAttachments(body) {
		meta := value.(map[string]interface{})
		var info attInfo
		info.contentType, _ = meta["content_type"].(string)
		info.data, _ = decodeAttachment(meta["data"])
		if info.data != nil && len(info.data) > kMaxInlineAttachmentSize {
			info.name = name
			following = append(following, info)
			delete(meta, "data")
			meta["follows"] = true
		}
	}

	// Write the main JSON body:
	jsonOut, _ := json.Marshal(body)
	partHeaders := textproto.MIMEHeader{}
	partHeaders.Set("Content-Type", "application/json")
	part, _ := writer.CreatePart(partHeaders)
	part.Write(jsonOut)

	// Write the following attachments
	for _, info := range following {
		partHeaders := textproto.MIMEHeader{}
		if info.contentType != "" {
			partHeaders.Set("Content-Type", info.contentType)
		}
		partHeaders.Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", info.name))
		part, _ := writer.CreatePart(partHeaders)
		part.Write(info.data)
	}
}
Пример #5
0
func createFormFile(writer *multipart.Writer, fieldname, filename string) {
	// Try to open the file.
	file, err := os.Open(filename)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	// Create a new form-data header with the provided field name and file name.
	// Determine Content-Type of the file by its extension.
	h := textproto.MIMEHeader{}
	h.Set("Content-Disposition", fmt.Sprintf(
		`form-data; name="%s"; filename="%s"`,
		escapeQuotes(fieldname),
		escapeQuotes(filepath.Base(filename)),
	))
	h.Set("Content-Type", "application/octet-stream")
	if ct := mime.TypeByExtension(filepath.Ext(filename)); ct != "" {
		h.Set("Content-Type", ct)
	}
	part, err := writer.CreatePart(h)
	if err != nil {
		panic(err)
	}

	// Copy the content of the file we have opened not reading the whole
	// file into memory.
	_, err = io.Copy(part, file)
	if err != nil {
		panic(err)
	}
}
Пример #6
0
func createBlobletPartWriter(i int, path string, size int64, hash string, writer *multipart.Writer) (io.Writer, error) {
	h := make(textproto.MIMEHeader)
	h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="bloblet-%d"; path="%s"; filename="%s"; hash="%s"`, i, path, bloblet.BlobletFileName, hash))
	h.Set("Content-Type", "application/zip")
	h.Set("Content-Length", fmt.Sprintf("%d", size))
	h.Set("Content-Transfer-Encoding", "binary")
	return writer.CreatePart(h)
}
Пример #7
0
func createZipPartWriter(zipStats os.FileInfo, writer *multipart.Writer) (io.Writer, error) {
	h := make(textproto.MIMEHeader)
	h.Set("Content-Disposition", `form-data; name="application"; filename="application.zip"`)
	h.Set("Content-Type", "application/zip")
	h.Set("Content-Length", fmt.Sprintf("%d", zipStats.Size()))
	h.Set("Content-Transfer-Encoding", "binary")
	return writer.CreatePart(h)
}
Пример #8
0
func createJpegFormFile(w *multipart.Writer, fieldname, filename string) (io.Writer, error) {
	h := make(textproto.MIMEHeader)
	h.Set("Content-Disposition",
		fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
			escapeQuotes(fieldname), escapeQuotes(filename)))
	// Major change from the stock function is to declare this with
	// type == image/jpeg, otherwise Shutterfly's API will choke on
	// the request:
	h.Set("Content-Type", "image/jpeg")
	return w.CreatePart(h)
}
Пример #9
0
/*
 * extra.CheckCrc:
 *      0:     不进行crc32校验
 *      1:     以writeMultipart自动生成crc32的值,进行校验
 *      2:     以extra.Crc32的值,进行校验
 *      other: 和2一样, 以 extra.Crc32的值,进行校验
 */
func writeMultipart(writer *multipart.Writer, uptoken, key string, hasKey bool, extra *PutExtra) (err error) {

	if extra == nil {
		extra = &PutExtra{}
	}

	//token
	if err = writer.WriteField("token", uptoken); err != nil {
		return
	}

	//key
	if hasKey {
		if err = writer.WriteField("key", key); err != nil {
			return
		}
	}

	// extra.Params
	if extra.Params != nil {
		for k, v := range extra.Params {
			err = writer.WriteField(k, v)
			if err != nil {
				return
			}
		}
	}

	//extra.CheckCrc
	if extra.CheckCrc != 0 {
		err = writer.WriteField("crc32", strconv.FormatInt(int64(extra.Crc32), 10))
		if err != nil {
			return
		}
	}

	//file
	head := make(textproto.MIMEHeader)

	// default the filename is same as key , but ""
	var fileName = key
	if fileName == "" {
		fileName = "filename"
	}

	head.Set("Content-Disposition",
		fmt.Sprintf(`form-data; name="file"; filename="%s"`, escapeQuotes(fileName)))
	if extra.MimeType != "" {
		head.Set("Content-Type", extra.MimeType)
	}

	_, err = writer.CreatePart(head)
	return err
}
Пример #10
0
// CreateFormFile creates a form file
func CreateFormFile(w *multipart.Writer, fieldname, filename, contentType string) (io.Writer, error) {
	h := make(textproto.MIMEHeader)
	h.Set("Content-Type", contentType)
	h.Set("Content-Disposition",
		fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
			escapeQuotes(fieldname), escapeQuotes(filename)))
	if contentType == "" {
		contentType = "application/octet-stream"
	}
	return w.CreatePart(h)
}
Пример #11
0
func multipartMIME(
	writer *multipart.Writer,
	msg string,
	attachments []*Attachment,
) error {
	// write message
	mh := make(textproto.MIMEHeader)
	mh.Add("Content-Type", "text/plain")
	mh.Add("Content-Transfer-Encoding", "base64")
	msgWriter, err := writer.CreatePart(mh)
	if err != nil {
		return log.Error(err)
	}
	_, err = io.WriteString(msgWriter, base64.Encode([]byte(msg)))
	if err != nil {
		return log.Error(err)
	}

	// write attachments
	for _, attachment := range attachments {
		mh = make(textproto.MIMEHeader)
		base := filepath.Base(attachment.Filename)
		if attachment.ContentType != "" {
			mh.Add("Content-Type", attachment.ContentType)
		} else {
			ct := mime.TypeByExtension(filepath.Ext(base))
			if ct != "" {
				mh.Add("Content-Type", ct)
			} else {
				mh.Add("Content-Type", "application/octet-stream")
			}
		}
		mh.Add("Content-Transfer-Encoding", "base64")
		mh.Add("Content-Disposition", "attachment; filename="+base)
		if attachment.Inline {
			mh.Add("Content-Disposition", "inline")
		}
		attachmentWriter, err := writer.CreatePart(mh)
		if err != nil {
			return log.Error(err)
		}
		encoder := base64.NewEncoder(attachmentWriter)
		if _, err := io.Copy(encoder, attachment.Reader); err != nil {
			return log.Error(err)
		}
		if err := encoder.Close(); err != nil {
			return log.Error(err)
		}
	}
	return nil
}
Пример #12
0
func appendPart(w *multipart.Writer, headers func(h textproto.MIMEHeader), body string) {
	if body == "" {
		return
	}

	h := textproto.MIMEHeader{}
	h.Set("Content-Transfer-Encoding", "quoted-printable")
	headers(h)
	partW, err := w.CreatePart(h)
	log.Panice(err, "create MIME part")
	quoW := quotedprintable.NewWriter(partW)
	defer quoW.Close()
	io.WriteString(quoW, body)
}
Пример #13
0
// Write the body of the email to the specified writer.
func (e *Email) writeBody(w *multipart.Writer) error {
	var (
		buff      = &bytes.Buffer{}
		altWriter = multipart.NewWriter(buff)
	)
	p, err := w.CreatePart(textproto.MIMEHeader{
		"Content-Type": []string{
			fmt.Sprintf("multipart/alternative; boundary=%s", altWriter.Boundary()),
		},
	})
	if err != nil {
		return err
	}
	if e.Text == "" {
		e.Text = sanitize.HTML(e.Html)
	}
	if e.Html == "" {
		e.Html = toHTML(e.Text)
	}
	if err := (Attachment{
		ContentType: "text/plain; charset=utf-8",
		Content:     e.Text,
	}.Write(altWriter)); err != nil {
		return err
	}
	if err := (Attachment{
		ContentType: "text/html; charset=utf-8",
		Content:     e.Html,
	}.Write(altWriter)); err != nil {
		return err
	}
	if err := altWriter.Close(); err != nil {
		return err
	}
	if _, err := io.Copy(p, buff); err != nil {
		return err
	}
	return nil
}
Пример #14
0
// Writes a revision to a MIME multipart writer, encoding large attachments as separate parts.
func (db *Database) WriteMultipartDocument(body Body, writer *multipart.Writer, compress bool) {
	// First extract the attachments that should follow:
	following := []attInfo{}
	for name, value := range BodyAttachments(body) {
		meta := value.(map[string]interface{})
		if meta["stub"] != true {
			var err error
			var info attInfo
			info.contentType, _ = meta["content_type"].(string)
			info.data, err = decodeAttachment(meta["data"])
			if info.data == nil {
				base.Warn("Couldn't decode attachment %q of doc %q: %v", name, body["_id"], err)
				meta["stub"] = true
				delete(meta, "data")
			} else if len(info.data) > kMaxInlineAttachmentSize {
				info.name = name
				following = append(following, info)
				meta["follows"] = true
				delete(meta, "data")
			}
		}
	}

	// Write the main JSON body:
	writeJSONPart(writer, "application/json", body, compress)

	// Write the following attachments
	for _, info := range following {
		partHeaders := textproto.MIMEHeader{}
		if info.contentType != "" {
			partHeaders.Set("Content-Type", info.contentType)
		}
		partHeaders.Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", info.name))
		part, _ := writer.CreatePart(partHeaders)
		part.Write(info.data)
	}
}
Пример #15
0
// Bytes gets the encoded MIME message.
func (m *Message) Bytes() ([]byte, error) {
	var buffer = &bytes.Buffer{}

	header := textproto.MIMEHeader{}

	var err error

	// Require To, Cc, or Bcc
	// We'll parse the slices into a list of addresses
	// and then make sure that list isn't empty.
	toAddrs := getAddressListString(m.To)
	ccAddrs := getAddressListString(m.Cc)
	bccAddrs := getAddressListString(m.Bcc)

	var hasTo = toAddrs != ""
	var hasCc = ccAddrs != ""
	var hasBcc = bccAddrs != ""

	if !hasTo && !hasCc && !hasBcc {
		return nil, ErrMissingRecipient
	}

	if hasTo {
		header.Add("To", toAddrs)
	}
	if hasCc {
		header.Add("Cc", ccAddrs)
	}
	// BCC header is excluded on purpose.
	// BCC recipients aren't included in the message
	// headers and are only used at the SMTP level.

	var emptyAddress mail.Address
	// Require From address
	if m.From == emptyAddress {
		return nil, ErrMissingFromAddress
	}
	header.Add("From", m.From.String())

	// Optional ReplyTo
	if m.ReplyTo != emptyAddress {
		header.Add("Reply-To", m.ReplyTo.String())
	}

	// Optional Subject
	if m.Subject != "" {
		quotedSubject := qEncodeAndWrap(m.Subject, 9 /* len("Subject: ") */)
		if quotedSubject[0] == '"' {
			// qEncode used simple quoting, which adds quote
			// characters to email subjects.
			quotedSubject = quotedSubject[1 : len(quotedSubject)-1]
		}
		header.Add("Subject", quotedSubject)
	}

	// Date
	if _, ok := m.Headers["Date"]; !ok {
		header.Add("Date", time.Now().UTC().Format(time.RFC822))
	}

	for k, v := range m.Headers {
		header[k] = v
	}

	header.Add("MIME-Version", "1.0")

	var mixedw *multipart.Writer
	var hasAttachments = m.Attachments != nil && len(m.Attachments) > 0
	if hasAttachments {
		// Top level multipart writer for our `multipart/mixed` body.
		// only needed if we have attachments
		mixedw = multipart.NewWriter(buffer)
		header.Add("Content-Type", fmt.Sprintf("multipart/mixed;%s boundary=%s", crlf, mixedw.Boundary()))
		err = writeHeader(buffer, header)
		if err != nil {
			return nil, err
		}
		header = textproto.MIMEHeader{}
		// Write the start of our `multipart/mixed` body.
		_, err = fmt.Fprintf(buffer, "--%s%s", mixedw.Boundary(), crlf)
		if err != nil {
			return nil, err
		}
	}

	var altw *multipart.Writer
	if m.Body != "" && m.HTMLBody != "" {
		// Nested multipart writer for our `multipart/alternative` body.
		altw = multipart.NewWriter(buffer)

		header.Add("Content-Type", fmt.Sprintf("multipart/alternative;%s boundary=%s", crlf, altw.Boundary()))
		err := writeHeader(buffer, header)
		if err != nil {
			return nil, err
		}
	}

	// Only include an empty plain text body if the html body is also empty.
	if m.Body != "" || m.Body == "" && m.HTMLBody == "" {
		if altw != nil {
			header = textproto.MIMEHeader{}
		}
		header.Add("Content-Type", "text/plain; charset=utf-8")
		header.Add("Content-Transfer-Encoding", "quoted-printable")
		//header.Add("Content-Transfer-Encoding", "base64")

		var writer io.Writer
		if altw != nil {
			partw, err := altw.CreatePart(header)
			if err != nil {
				return nil, err
			}
			writer = partw
		} else {
			writer = buffer
			err = writeHeader(buffer, header)
			if err != nil {
				return nil, err
			}
		}

		bodyBytes := []byte(m.Body)
		//encoder := NewBase64MimeEncoder(writer)
		encoder := qprintable.NewEncoder(qprintable.DetectEncoding(m.Body), writer)
		_, err = encoder.Write(bodyBytes)
		if err != nil {
			return nil, err
		}
		err = encoder.Close()
		if err != nil {
			return nil, err
		}
	}

	if m.HTMLBody != "" {
		if altw != nil {
			header = textproto.MIMEHeader{}
		}
		header.Add("Content-Type", "text/html; charset=utf-8")
		//header.Add("Content-Transfer-Encoding", "quoted-printable")
		header.Add("Content-Transfer-Encoding", "base64")

		var writer io.Writer
		if altw != nil {
			partw, err := altw.CreatePart(header)
			if err != nil {
				return nil, err
			}
			writer = partw
		} else {
			writer = buffer
			err = writeHeader(buffer, header)
			if err != nil {
				return nil, err
			}
		}

		htmlBodyBytes := []byte(m.HTMLBody)
		encoder := NewBase64MimeEncoder(writer)
		//encoder := qprintable.NewEncoder(qprintable.DetectEncoding(m.HTMLBody), writer)
		_, err = encoder.Write(htmlBodyBytes)
		if err != nil {
			return nil, err
		}
		err = encoder.Close()
		if err != nil {
			return nil, err
		}
	}

	if altw != nil {
		altw.Close()
	} else {
		_, err = fmt.Fprintf(buffer, "%s", crlf)
		if err != nil {
			return nil, err
		}
	}

	if hasAttachments {

		for _, attachment := range m.Attachments {

			contentType := attachment.ContentType
			if contentType == "" {
				contentType = mime.TypeByExtension(filepath.Ext(attachment.Name))
				if contentType == "" {
					contentType = "application/octet-stream"
				}
			}

			header := textproto.MIMEHeader{}
			header.Add("Content-Type", contentType)
			header.Add("Content-Disposition", fmt.Sprintf(`attachment;%s filename="%s"`, crlf, attachment.Name))
			header.Add("Content-Transfer-Encoding", "base64")

			attachmentPart, err := mixedw.CreatePart(header)
			if err != nil {
				return nil, err
			}

			if attachment.Data != nil {
				encoder := NewBase64MimeEncoder(attachmentPart)
				_, err = io.Copy(encoder, attachment.Data)
				if err != nil {
					return nil, err
				}
				err = encoder.Close()
				if err != nil {
					return nil, err
				}
			}
		}

		mixedw.Close()
	}

	return buffer.Bytes(), nil
}
Пример #16
0
func sendMail(to []string, cc []string, bcc []string, msg *Message) error {
	from := Config.DefaultFrom
	server := Config.MailServer
	if msg.Server != "" {
		server = msg.Server
	}
	if msg.From != "" {
		from = msg.From
	}
	if from == "" {
		return errNoFrom
	}
	var auth smtp.Auth
	cram, username, password, server := parseServer(server)
	if username != "" || password != "" {
		if cram {
			auth = smtp.CRAMMD5Auth(username, password)
		} else {
			auth = smtp.PlainAuth("", username, password, server)
		}
	}
	var buf bytes.Buffer
	headers := msg.Headers
	if headers == nil {
		headers = make(Headers)
	}
	if msg.Subject != "" {
		headers["Subject"] = msg.Subject
	}
	headers["From"] = from
	if msg.ReplyTo != "" {
		headers["Reply-To"] = msg.ReplyTo
	}
	var err error
	if len(to) > 0 {
		headers["To"], err = joinAddrs(to)
		if err != nil {
			return err
		}
		for ii, v := range to {
			if v == Admin {
				if Config.AdminEmail == "" {
					return errNoAdminEmail
				}
				to[ii] = Config.AdminEmail
			}
		}
	}
	if len(cc) > 0 {
		headers["Cc"], err = joinAddrs(cc)
		if err != nil {
			return err
		}
	}
	if len(bcc) > 0 {
		headers["Bcc"], err = joinAddrs(bcc)
		if err != nil {
			return err
		}
	}
	hk := make([]string, 0, len(headers))
	for k := range headers {
		hk = append(hk, k)
	}
	sort.Strings(hk)
	for _, k := range hk {
		buf.WriteString(fmt.Sprintf("%s: %s\r\n", k, headers[k]))
	}
	buf.WriteString("MIME-Version: 1.0\r\n")
	mw := multipart.NewWriter(&buf)
	mw.SetBoundary(makeBoundary())
	// Create a multipart mixed first
	fmt.Fprintf(&buf, "Content-Type: multipart/mixed;\r\n\tboundary=%q\r\n\r\n", mw.Boundary())
	var bodyWriter *multipart.Writer
	if msg.TextBody != "" && msg.HTMLBody != "" {
		boundary := makeBoundary()
		outerHeader := make(textproto.MIMEHeader)
		// First part is a multipart/alternative, which contains the text and html bodies
		outerHeader.Set("Content-Type", fmt.Sprintf("multipart/alternative; boundary=%q", boundary))
		iw, err := mw.CreatePart(outerHeader)
		if err != nil {
			return err
		}
		bodyWriter = multipart.NewWriter(iw)
		bodyWriter.SetBoundary(boundary)
	} else {
		bodyWriter = mw
	}
	if msg.TextBody != "" {
		textHeader := make(textproto.MIMEHeader)
		textHeader.Set("Content-Type", "text/plain; charset=UTF-8")
		tpw, err := bodyWriter.CreatePart(textHeader)
		if err != nil {
			return err
		}
		if _, err := io.WriteString(tpw, msg.TextBody); err != nil {
			return err
		}
		tpw.Write(crlf)
		tpw.Write(crlf)
	}
	attached := make(map[*Attachment]bool)
	if msg.HTMLBody != "" {
		var htmlAttachments []*Attachment
		for _, v := range msg.Attachments {
			if v.ContentID != "" && strings.Contains(msg.HTMLBody, fmt.Sprintf("cid:%s", v.ContentID)) {
				htmlAttachments = append(htmlAttachments, v)
				attached[v] = true
			}
		}
		var htmlWriter *multipart.Writer
		if len(htmlAttachments) > 0 {
			relatedHeader := make(textproto.MIMEHeader)
			relatedBoundary := makeBoundary()
			relatedHeader.Set("Content-Type", fmt.Sprintf("multipart/related; boundary=%q; type=\"text/html\"", relatedBoundary))
			rw, err := bodyWriter.CreatePart(relatedHeader)
			if err != nil {
				return err
			}
			htmlWriter = multipart.NewWriter(rw)
			htmlWriter.SetBoundary(relatedBoundary)
		} else {
			htmlWriter = bodyWriter
		}
		htmlHeader := make(textproto.MIMEHeader)
		htmlHeader.Set("Content-Type", "text/html; charset=UTF-8")
		thw, err := htmlWriter.CreatePart(htmlHeader)
		if err != nil {
			return err
		}
		if _, err := io.WriteString(thw, msg.HTMLBody); err != nil {
			return err
		}
		thw.Write(crlf)
		thw.Write(crlf)
		for _, v := range htmlAttachments {
			attachmentHeader := make(textproto.MIMEHeader)
			attachmentHeader.Set("Content-Disposition", "inline")
			attachmentHeader.Set("Content-Id", fmt.Sprintf("<%s>", v.ContentID))
			attachmentHeader.Set("Content-Transfer-Encoding", "base64")
			attachmentHeader.Set("Content-Type", v.ContentType)
			aw, err := htmlWriter.CreatePart(attachmentHeader)
			if err != nil {
				return err
			}
			b := make([]byte, base64.StdEncoding.EncodedLen(len(v.Data)))
			base64.StdEncoding.Encode(b, v.Data)
			aw.Write(b)
		}
		if htmlWriter != bodyWriter {
			if err := htmlWriter.Close(); err != nil {
				return err
			}
		}
	}
	if bodyWriter != mw {
		if err := bodyWriter.Close(); err != nil {
			return err
		}
	}
	for _, v := range msg.Attachments {
		if attached[v] {
			continue
		}
		attachmentHeader := make(textproto.MIMEHeader)
		attachmentHeader.Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", v.Name))
		attachmentHeader.Set("Content-Transfer-Encoding", "base64")
		attachmentHeader.Set("Content-Type", v.ContentType)
		aw, err := mw.CreatePart(attachmentHeader)
		if err != nil {
			return err
		}
		b := make([]byte, base64.StdEncoding.EncodedLen(len(v.Data)))
		base64.StdEncoding.Encode(b, v.Data)
		aw.Write(b)
	}
	if err := mw.Close(); err != nil {
		return err
	}
	if server == "echo" {
		printer(buf.String())
		return nil
	}
	return smtp.SendMail(server, auth, from, to, buf.Bytes())
}
Пример #17
0
func CreateFormFile(w *multipart.Writer, filename string) (io.Writer, error) {
	h := make(textproto.MIMEHeader)
	h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"; filename="%s"`, "file", filename))
	h.Set("Content-Type", "application/zip")
	return w.CreatePart(h)
}