func mediaTypeOrDefault(header textproto.MIMEHeader) string { mediaType, _, err := mime.ParseMediaType(header.Get("Content-Type")) if err != nil { return "application/octet-stream" } return mediaType }
func checkContentType(h textproto.MIMEHeader, expected string) error { ctype := h.Get("Content-Type") if ctype != expected { return errors.Errorf("expected Content-Type %q, got %q", expected, ctype) } return nil }
// Date parses the Date header field. func Date(h textproto.MIMEHeader) (time.Time, error) { hdr := h.Get("Date") if hdr == "" { return time.Time{}, ErrHeaderNotPresent } return parseDate(hdr) }
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 }
func newEntity(h textproto.MIMEHeader, r io.Reader, shared *sharedData) (*Entity, error) { contType := h.Get("Content-Type") if contType == "" { return &Entity{ Header: h, body: &SinglepartBody{Reader: r}, shared: shared, }, nil } mediaType, params, err := mime.ParseMediaType(contType) if err != nil { return nil, err } if !strings.HasPrefix(mediaType, "multipart/") { return &Entity{ Header: h, body: &SinglepartBody{Reader: r}, shared: shared, }, nil } boundary, ok := params["boundary"] if !ok { return nil, fmt.Errorf("Boundary not found in Content-Type field: %v", contType) } return &Entity{ Header: h, body: &MultipartBody{multipart.NewReader(r, boundary)}, shared: shared, }, nil }
// Very Useful for AJAX Validater that require server-side validation func (f *Form) ValidateSingle(name, value, mime string) error { values := Values{ name: []string{value}, } mimeheader := textproto.MIMEHeader{} mimeheader.Add("Content-Type", mime) files := FileHeaders{ name: []*multipart.FileHeader{&multipart.FileHeader{ Header: mimeheader}}, } val := &Validation{values, files, true, CurVal("")} for _, field := range f.fields { switch t := field.(type) { case Label: continue default: if t.GetName() == name { return t.Validate(val) } } } return FormError(f.lang["ErrFieldDoesNotExist"]) }
// 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{}) 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: 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) } }
//handleEventMsg processes event messages received from Freeswitch. func (client *Client) handleEventMsg(resp textproto.MIMEHeader) error { event := make(map[string]string) //Check that Content-Length is numeric. _, err := strconv.Atoi(resp.Get("Content-Length")) if err != nil { log.Print(logPrefix, "Invalid Content-Length", err) return err } for { //Read each line of the event and store into map. line, err := client.eventConn.ReadLine() if err != nil { log.Print(logPrefix, "Event Read failure: ", err) return err } if line == "" { //Empty line means end of event. client.sendEvent(event) return err } parts := strings.Split(line, ": ") //Split "Key: value" key := parts[0] value, err := url.QueryUnescape(parts[1]) if err != nil { log.Print(logPrefix, "Parse failure: ", err) return err } event[key] = value } }
// 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) } }
// CreateMailMessage creates a (plain) SMTP email with body and // optional attachments. func CreateMailMessage(body []byte, att []*MailAttachment) ([]byte, error) { buf := new(bytes.Buffer) wrt := multipart.NewWriter(buf) buf.WriteString( "MIME-Version: 1.0\n" + "Content-Type: multipart/mixed;\n" + " boundary=\"" + wrt.Boundary() + "\"\n\n" + "This is a multi-part message in MIME format.\n") hdr := textproto.MIMEHeader{} hdr.Set("Content-Type", "text/plain; charset=ISO-8859-15") hdr.Set("Content-Transfer-Encoding", "utf-8") pw, err := wrt.CreatePart(hdr) if err != nil { return nil, err } pw.Write(body) for _, a := range att { pw, err = wrt.CreatePart(a.Header) if err != nil { return nil, err } pw.Write(a.Data) } wrt.Close() return buf.Bytes(), nil }
func renderPartsToWriter(parts cloudInitParts, writer io.Writer) error { mimeWriter := multipart.NewWriter(writer) defer mimeWriter.Close() writer.Write([]byte(fmt.Sprintf("Content-Type: multipart/mixed; boundary=\"%s\"\n", mimeWriter.Boundary()))) for _, part := range parts { header := textproto.MIMEHeader{} if part.ContentType == "" { header.Set("Content-Type", "text/plain") } else { header.Set("Content-Type", part.ContentType) } if part.Filename != "" { header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, part.Filename)) } if part.MergeType != "" { header.Set("X-Merge-Type", part.MergeType) } partWriter, err := mimeWriter.CreatePart(header) if err != nil { return err } _, err = partWriter.Write([]byte(part.Content)) if err != nil { return err } } return nil }
// parseMIMEParts will recursively walk a MIME entity and return a []mime.Part containing // each (flattened) mime.Part found. // It is important to note that there are no limits to the number of recursions, so be // careful when parsing unknown MIME structures! func parseMIMEParts(hs textproto.MIMEHeader, b io.Reader) ([]*part, error) { var ps []*part // If no content type is given, set it to the default if _, ok := hs["Content-Type"]; !ok { hs.Set("Content-Type", defaultContentType) } ct, params, err := mime.ParseMediaType(hs.Get("Content-Type")) if err != nil { return ps, err } // If it's a multipart email, recursively parse the parts if strings.HasPrefix(ct, "multipart/") { if _, ok := params["boundary"]; !ok { return ps, ErrMissingBoundary } mr := multipart.NewReader(b, params["boundary"]) for { var buf bytes.Buffer p, err := mr.NextPart() if err == io.EOF { break } if err != nil { return ps, err } if _, ok := p.Header["Content-Type"]; !ok { p.Header.Set("Content-Type", defaultContentType) } subct, _, err := mime.ParseMediaType(p.Header.Get("Content-Type")) if strings.HasPrefix(subct, "multipart/") { sps, err := parseMIMEParts(p.Header, p) if err != nil { return ps, err } ps = append(ps, sps...) } else { // Otherwise, just append the part to the list // Copy the part data into the buffer if _, err := io.Copy(&buf, p); err != nil { return ps, err } ps = append(ps, &part{body: buf.Bytes(), header: p.Header}) } } } else { // If it is not a multipart email, parse the body content as a single "part" var buf bytes.Buffer if _, err := io.Copy(&buf, b); err != nil { return ps, err } ps = append(ps, &part{body: buf.Bytes(), header: hs}) } return ps, nil }
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) }
//handleAPIMsg processes API response messages received from Freeswitch. func (client *Client) handleAPIMsg(resp textproto.MIMEHeader) error { //Check that Content-Length is numeric. length, err := strconv.Atoi(resp.Get("Content-Length")) if err != nil { log.Print(logPrefix, "Invalid Content-Length", err) client.sendCmdRes(cmdRes{body: "", err: err}, true) return err } //Read Content-Length bytes into a buffer and convert to string. buf := make([]byte, length) if _, err = io.ReadFull(client.eventConn.R, buf); err != nil { log.Print(logPrefix, "API Read failure: ", err) } client.sendCmdRes(cmdRes{body: string(buf), err: err}, true) return err }
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) } }
func TestSubjectHeaderWithExistingQuotes(t *testing.T) { m := simpleMessage() m.Subject = `"Hi World"` buf := new(bytes.Buffer) header := textproto.MIMEHeader{} _, err := m.bytes(buf, header) if err != nil { t.Log(err) t.Fail() } expected := `\"Hi World\"` if sub := header.Get("Subject"); sub != expected { t.Logf(`Expected Subject to be "%s" but got "%s"`, expected, sub) t.Fail() } }
// store message, unpack attachments, register with daemon, send to daemon for federation // in that order func (self *nntpConnection) storeMessage(daemon *NNTPDaemon, hdr textproto.MIMEHeader, body io.Reader) (err error) { var f io.WriteCloser msgid := getMessageID(hdr) if msgid == "" { // drop, invalid header log.Println(self.name, "dropping message with invalid mime header, no message-id") _, err = io.Copy(Discard, body) return } else if ValidMessageID(msgid) { f = daemon.store.CreateFile(msgid) } else { // invalid message-id log.Println(self.name, "dropping message with invalid message-id", msgid) _, err = io.Copy(Discard, body) return } if f == nil { // could not open file, probably already storing it from another connection log.Println(self.name, "discarding duplicate message") _, err = io.Copy(Discard, body) return } path := hdr.Get("Path") hdr.Set("Path", daemon.instance_name+"!"+path) // now store attachments and article err = writeMIMEHeader(f, hdr) if err == nil { err = daemon.store.ProcessMessageBody(f, hdr, body) if err == nil { // tell daemon daemon.loadFromInfeed(msgid) } } f.Close() if err != nil { // clean up if ValidMessageID(msgid) { DelFile(daemon.store.GetFilename(msgid)) } } return }
// get a message id from a mime header // checks many values func getMessageID(hdr textproto.MIMEHeader) (msgid string) { msgid = hdr.Get("Message-Id") if msgid == "" { msgid = hdr.Get("Message-ID") } if msgid == "" { msgid = hdr.Get("message-id") } if msgid == "" { msgid = hdr.Get("MESSAGE-ID") } return }
// Read body from text/plain func readPlainText(header textproto.MIMEHeader, body io.Reader) (mailbody []byte, err error) { contentType := header.Get("Content-Type") encoding := header.Get("Content-Transfer-Encoding") _, params, err := mime.ParseMediaType(contentType) if encoding == ENC_QUOTED_PRINTABLE { if strings.ToLower(params["charset"]) == CHARSET_ISO2022JP { mailbody, err = ioutil.ReadAll(transform.NewReader(quotedprintable.NewReader(body), japanese.ISO2022JP.NewDecoder())) } else { mailbody, err = ioutil.ReadAll(quotedprintable.NewReader(body)) } } else if encoding == ENC_BASE64 { mailbody, err = ioutil.ReadAll(base64.NewDecoder(base64.StdEncoding, body)) } else if len(contentType) == 0 || strings.ToLower(params["charset"]) == CHARSET_ISO2022JP { // charset=ISO-2022-JP mailbody, err = ioutil.ReadAll(transform.NewReader(body, japanese.ISO2022JP.NewDecoder())) } else { // encoding = 8bit or 7bit mailbody, err = ioutil.ReadAll(body) } return mailbody, err }
func TestPostBeacon(t *testing.T) { imgData = strings.Replace(imgData, "\n", "", -1) imgBytes, err := hex.DecodeString(imgData) if err != nil { t.Fatalf("Unable to parse image data.") } body := &bytes.Buffer{} partWriter := multipart.NewWriter(body) jsonHeader := textproto.MIMEHeader{} jsonHeader.Add("Content-Type", "application/json") jsonWriter, err := partWriter.CreatePart(jsonHeader) io.WriteString(jsonWriter, jsonData) imgHeader := textproto.MIMEHeader{} imgHeader.Add("Content-Type", "img/jpeg") imgWriter, err := partWriter.CreatePart(imgHeader) imgWriter.Write(imgBytes) partWriter.Close() req, _ := http.NewRequest("POST", "http://localhost:8765/beacon", body) req.Header.Add("Content-Type", partWriter.FormDataContentType()) req.SetBasicAuth("1", "0") client := &http.Client{} resp, err := client.Do(req) if err != nil { t.Error(err.Error()) } respBody, err := ioutil.ReadAll(resp.Body) if err != nil { t.Error(err.Error()) } if !strings.Contains(string(respBody), "id") { t.Fatalf("Response did not contain correct content: \n%s", string(respBody)) } }
func HandleGetBeacon(w http.ResponseWriter, r *http.Request, id uint64, db *DBClient) { var viewerID int64 viewerID, err := OptionalAuthenticate(w, r, db) beacon, err := db.GetThread(id) if err != nil { WriteErrorResp(w, err.Error(), DatabaseError) return } respBeaconMsg, err := ToRespBeaconMsg(w, beacon, viewerID, db) if err != nil { return } respJson, err := json.Marshal(respBeaconMsg) respBody := &bytes.Buffer{} partWriter := multipart.NewWriter(respBody) jsonHeader := textproto.MIMEHeader{} jsonHeader.Add("Content-Type", "application/json") jsonWriter, err := partWriter.CreatePart(jsonHeader) if err != nil { WriteErrorResp(w, err.Error(), ServerError) return } jsonWriter.Write(respJson) imgHeader := textproto.MIMEHeader{} imgHeader.Add("Content-Type", "img/jpeg") imgWriter, err := partWriter.CreatePart(imgHeader) if err != nil { WriteErrorResp(w, err.Error(), ServerError) return } imgWriter.Write(beacon.Image) partWriter.Close() w.Header().Add("Content-Type", partWriter.FormDataContentType()) w.Write(respBody.Bytes()) }
// parseMIMEParts will recursively walk a MIME entity and return a []mime.Part containing // each (flattened) mime.Part found. // It is important to note that there are no limits to the number of recursions, so be // careful when parsing unknown MIME structures! func parseMIMEParts(hs textproto.MIMEHeader, b io.Reader) ([]*part, error) { var ps []*part ct, params, err := mime.ParseMediaType(hs.Get("Content-Type")) if err != nil { return ps, err } if strings.HasPrefix(ct, "multipart/") { if _, ok := params["boundary"]; !ok { return ps, ErrMissingBoundary } mr := multipart.NewReader(b, params["boundary"]) for { var buf bytes.Buffer p, err := mr.NextPart() if err == io.EOF { break } if err != nil { return ps, err } subct, _, err := mime.ParseMediaType(p.Header.Get("Content-Type")) if strings.HasPrefix(subct, "multipart/") { sps, err := parseMIMEParts(p.Header, p) if err != nil { return ps, err } ps = append(ps, sps...) } else { // Otherwise, just append the part to the list // Copy the part data into the buffer if _, err := io.Copy(&buf, p); err != nil { return ps, err } ps = append(ps, &part{body: buf.Bytes(), header: p.Header}) } } } return ps, nil }
// Bytes converts the Email object to a []byte representation, including all needed MIMEHeaders, boundaries, etc. func (m *Message) Bytes() ([]byte, error) { // TODO: better guess buffer size buff := bytes.NewBuffer(make([]byte, 0, 4096)) headers := m.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(m.Content) > 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(m.Content) > 0 { header.Set("Content-Type", fmt.Sprintf("%s; charset=UTF-8", m.ContentType)) header.Set("Content-Transfer-Encoding", "quoted-printable") if _, err := subWriter.CreatePart(header); err != nil { return nil, err } // Write the text if err := quotePrintEncode(buff, []byte(m.Content)); err != nil { return nil, err } } if err := subWriter.Close(); err != nil { return nil, err } } // Create attachment part, if necessary for _, a := range m.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 }
// AddressList parses the named header field as a list of addresses. func AddressList(header textproto.MIMEHeader, key string, utf8ReaderFactory UTF8ReaderFactory) (r []*Address, err error) { // h := make(map[string][]string) // vals := header[key] // if vals == nil { // return // } // var newDecodedVals []string // for _, val := range vals { // var newVal string // newVal, err = DecodeText(val, utf8ReaderFactory) // if err != nil { // return // } // newDecodedVals = append(newDecodedVals, newVal) // } // h[key] = newDecodedVals hdr := header.Get(key) if hdr == "" { return nil, ErrHeaderNotPresent } return newAddrParser(hdr, utf8ReaderFactory).parseAddressList() }
//newEvent build event func newEvent(data *textproto.MIMEHeader) (*AMIEvent, error) { if data.Get("Event") == "" { return nil, errNotEvent } ev := &AMIEvent{data.Get("Event"), strings.Split(data.Get("Privilege"), ","), make(map[string]string)} for k, v := range *data { if k == "Event" || k == "Privilege" { continue } ev.Params[k] = v[0] } return ev, nil }
//newResponse build a response for action func newResponse(data *textproto.MIMEHeader) (*AMIResponse, error) { if data.Get("Response") == "" { return nil, errors.New("Not Response") } response := &AMIResponse{"", "", make(map[string]string)} for k, v := range *data { if k == "Response" { continue } response.Params[k] = v[0] } response.ID = data.Get("Actionid") response.Status = data.Get("Response") return response, nil }
func renderPartsToWriter(parts cloudInitParts, writer io.Writer) error { mimeWriter := multipart.NewWriter(writer) defer mimeWriter.Close() // we need to set the boundary explictly, otherwise the boundary is random // and this causes terraform to complain about the resource being different if err := mimeWriter.SetBoundary("MIMEBOUNDARY"); err != nil { return err } writer.Write([]byte(fmt.Sprintf("Content-Type: multipart/mixed; boundary=\"%s\"\n", mimeWriter.Boundary()))) writer.Write([]byte("MIME-Version: 1.0\r\n")) for _, part := range parts { header := textproto.MIMEHeader{} if part.ContentType == "" { header.Set("Content-Type", "text/plain") } else { header.Set("Content-Type", part.ContentType) } header.Set("MIME-Version", "1.0") header.Set("Content-Transfer-Encoding", "7bit") if part.Filename != "" { header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, part.Filename)) } if part.MergeType != "" { header.Set("X-Merge-Type", part.MergeType) } partWriter, err := mimeWriter.CreatePart(header) if err != nil { return err } _, err = partWriter.Write([]byte(part.Content)) if err != nil { return err } } return nil }
func (handler *HttpHandler) ServeDataObj(obj *DataObj) { rng := handler.request.Header["Range"] objMime := handler.getObjMime(obj) lenStr := strconv.FormatInt(obj.Size(), 10) if len(rng) > 0 { rangeHeader := rng[0] byteRange := strings.Split(rangeHeader, "bytes=") if len(byteRange) == 2 && byteRange[0] == "" && byteRange[1] != "" { var outputBuffer RangeOutput byteRangeSplit := strings.Split(byteRange[1], ",") for _, rangeStr := range byteRangeSplit { rangeStrSplit := strings.Split(rangeStr, "-") if len(rangeStrSplit) == 2 { var convErr error var firstByteN int64 var lastByteN int64 firstByte := rangeStrSplit[0] lastByte := rangeStrSplit[1] // should we only get last bytes if firstByte == "" { if lastByteN, convErr = strconv.ParseInt(lastByte, 10, 64); convErr != nil { log.Print("Error parsing byte range") return } firstByteN = obj.Size() - lastByteN lastByteN = obj.Size() - 1 } else if lastByte == "" { if firstByteN, convErr = strconv.ParseInt(firstByte, 10, 64); convErr != nil { log.Print("Error parsing byte range") return } lastByteN = obj.Size() - 1 } else { if firstByteN, convErr = strconv.ParseInt(firstByte, 10, 64); convErr != nil { log.Print("Error parsing byte range") return } if lastByteN, convErr = strconv.ParseInt(lastByte, 10, 64); convErr != nil { log.Print("Error parsing byte range") return } } if byteData, err := obj.ReadBytes(firstByteN, int(lastByteN-firstByteN)+1); err == nil { outputBuffer = append(outputBuffer, RangeSegmentOutput{ ContentRange: "bytes " + strconv.FormatInt(firstByteN, 10) + "-" + strconv.FormatInt(lastByteN, 10) + "/" + lenStr, ContentType: objMime, ByteContent: byteData, }) } else { log.Print(err) } } else { log.Print("Error parsing byte range") } } if len(outputBuffer) > 1 { handler.response.Header().Set("Accept-Ranges", "bytes") //handler.response.Header().Set("Content-Length", outputBuffer.TotalLength()) mpWriter := multipart.NewWriter(handler.response) handler.response.Header().Set("Content-Type", "multipart/byteranges; boundary="+mpWriter.Boundary()) handler.response.WriteHeader(http.StatusPartialContent) for _, outputSegment := range outputBuffer { var headers textproto.MIMEHeader = make(textproto.MIMEHeader) headers.Add("Content-Type", outputSegment.ContentType) headers.Add("Content-Range", outputSegment.ContentRange) if writer, err := mpWriter.CreatePart(headers); err != nil { log.Print(err) continue } else { writer.Write(outputSegment.ByteContent) } } } else if len(outputBuffer) == 1 { handler.response.Header().Set("Content-Range", outputBuffer[0].ContentRange) handler.response.Header().Set("Accept-Ranges", "bytes") handler.response.Header().Set("Content-Length", strconv.Itoa(len(outputBuffer[0].ByteContent))) handler.response.Header().Set("Content-Type", outputBuffer[0].ContentType) handler.response.WriteHeader(http.StatusPartialContent) handler.response.Write(outputBuffer[0].ByteContent) } } } else { if handler.opts.Download || handler.query.Get("download") != "" { handler.response.Header().Set("Content-Disposition", "attachment; filename="+obj.Name()) handler.response.Header().Set("Content-type", "application/octet-stream") } else { handler.response.Header().Set("Content-type", objMime) } handler.response.Header().Set("Accept-Ranges", "bytes") handler.response.Header().Set("Content-Length", lenStr) if readEr := obj.ReadChunk(1024000, func(chunk []byte) { handler.response.Write(chunk) }); readEr != nil { log.Print(readEr) handler.response.WriteHeader(http.StatusInternalServerError) handler.response.Write([]byte("Error: " + readEr.Error())) } } }
// Bytes converts the Email object to a []byte representation, including all needed MIMEHeaders, boundaries, etc. func (e *Email) Bytes() ([]byte, error) { buff := &bytes.Buffer{} w := multipart.NewWriter(buff) // Set the appropriate headers (overwriting any conflicts) // Leave out Bcc (only included in envelope headers) e.Headers.Set("To", strings.Join(e.To, ",")) if e.Cc != nil { e.Headers.Set("Cc", strings.Join(e.Cc, ",")) } e.Headers.Set("From", e.From) e.Headers.Set("Subject", e.Subject) if len(e.ReadReceipt) != 0 { e.Headers.Set("Disposition-Notification-To", strings.Join(e.ReadReceipt, ",")) } e.Headers.Set("MIME-Version", "1.0") e.Headers.Set("Content-Type", fmt.Sprintf("multipart/mixed;\r\n boundary=%s\r\n", w.Boundary())) // Write the envelope headers (including any custom headers) if err := headerToBytes(buff, e.Headers); err != nil { return nil, fmt.Errorf("Failed to render message headers: %s", err) } // 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 e.Text != "" || e.HTML != "" { 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 if err := headerToBytes(buff, header); err != nil { return nil, fmt.Errorf("Failed to render multipart message headers: %s", err) } // Create the body sections if e.Text != "" { 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 } // Write the text if err := quotePrintEncode(buff, e.Text); err != nil { return nil, err } } if e.HTML != "" { 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 } // Write the text if err := quotePrintEncode(buff, e.HTML); 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 }
func SendMailWithAttachments(host string, auth *smtp.Auth, from, subject string, to []string, msg []byte, atch Attachments) error { c, err := SMTPConnection(host, auth) if err != nil { return err } defer c.Quit() if err := c.Mail(from); err != nil { return err } for _, addr := range to { if err := c.Rcpt(addr); err != nil { return err } } w, err := c.Data() if err != nil { return err } multiw := multipart.NewWriter(w) err = write( w, fmt.Sprintf("From: %s%s", from, CRLF), fmt.Sprintf("Subject: %s%s", subject, CRLF), fmt.Sprintf("To: %s%s", strings.Join(to, ","), CRLF), ) if err != nil { return err } if atch != nil { err = write( w, fmt.Sprintf(`Content-Type: multipart/mixed; boundary="%s"%s`, multiw.Boundary(), CRLF), "--"+multiw.Boundary()+CRLF, "Content-Transfer-Encoding: quoted-printable", ) } else { return write(w, strings.Repeat(CRLF, 4), string(msg), strings.Repeat(CRLF, 4)) } // We write either the message, or 4*CRLF since SMTP supports files // being sent without an actual body. if msg != nil { err = write(w, fmt.Sprintf( "%s%s%s", strings.Repeat(CRLF, 2), msg, strings.Repeat(CRLF, 2), ), ) if err != nil { return err } } else { if err := write(w, strings.Repeat(CRLF, 4)); err != nil { return err } } for filename, file := range atch { ext := mime.TypeByExtension(filepath.Ext(filename)) if ext == "" { ext = "text/plain" } h := textproto.MIMEHeader{} h.Add("Content-Type", ext) h.Add("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, filename)) h.Add("Content-Transfer-Encoding", "base64") newpart, err := multiw.CreatePart(h) if err != nil { return err } buf := bytes.NewBuffer([]byte{}) bcdr := NewBase64Email(buf, base64.StdEncoding) if _, err = io.Copy(bcdr, file); err != nil { return err } if err = bcdr.Close(); err != nil { return err } if _, err = io.Copy(newpart, buf); err != nil { return err } } if err = multiw.Close(); err != nil { return err } return w.Close() }