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()) }
// 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"]) }
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 (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())) } } }
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() }
func (s *Mailer) SendMail(email *shared.Email) error { if s.Config.DevOverrideAddress != nil && len(*s.Config.DevOverrideAddress) > 0 { email.HTML = email.Recipient + "<hr>" + email.HTML email.Recipient = *s.Config.DevOverrideAddress } log.Printf("Dial %s:%s", s.Config.ServerAddress, s.Config.ServerPort) c, err := smtp.Dial(s.Config.ServerAddress + ":" + s.Config.ServerPort) if err != nil { if s.Config.ServerPort == "9999" { log.Println("No dev email server is active") log.Printf(` MAILTO: %s SUBJECT: %s %s`, email.Recipient, email.Subject, email.HTML) return nil } log.Println(err) return err } defer c.Quit() // For Testing log.Println("Connected") log.Println("EHLO") if err = c.Hello(s.Config.EhloAddress); err != nil { log.Println(err) return err } log.Println("EHLO Done") if s.Config.ServerPort != "9999" { log.Println("Start TLS") tlsConfig := tls.Config{ ServerName: s.Config.ServerAddress, } if err = c.StartTLS(&tlsConfig); err != nil { err = fmt.Errorf("SMTP TLS Error: %s", err.Error()) log.Println(err) return err } auth := smtp.PlainAuth("", s.Config.Username, s.Config.Password, s.Config.ServerAddress) if err = c.Auth(auth); err != nil { err = fmt.Errorf("SMTP Auth error: %s", err.Error()) log.Println(err) return err } } log.Printf("Sender %s", email.Sender) if err = c.Mail(email.Sender); err != nil { c.Reset() log.Println(err) return err } log.Printf("Recipient %s", email.Recipient) if err = c.Rcpt(email.Recipient); err != nil { c.Reset() log.Println(err) return err } log.Println("Data") writer, err := c.Data() if err != nil { return fmt.Errorf("SMPT Data Error: %s", err.Error()) } mw := multipart.NewWriter(writer) headers := map[string]string{ "From": email.Sender, "To": email.Recipient, "Subject": email.Subject, "MIME-Version": "1.0", "Content-Type": `multipart/mixed; boundary="` + mw.Boundary() + `"`, } for key, val := range headers { fmt.Fprintf(writer, "%s: %s\n", key, val) } fmt.Fprintln(writer, "") htmlHeader := textproto.MIMEHeader{} htmlHeader.Add("Content-Type", "text/html") htmlPart, err := mw.CreatePart(htmlHeader) if err != nil { return err } fmt.Fprintln(htmlPart, email.HTML) mw.Close() writer.Close() c.Reset() log.Println("DONE") return nil }
// 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) } for k, v := range m.Headers { header[k] = v } // Top level multipart writer for our `multipart/mixed` body. mixedw := multipart.NewWriter(buffer) header.Add("MIME-Version", "1.0") header.Add("Content-Type", fmt.Sprintf("multipart/mixed;%s boundary=%s", crlf, mixedw.Boundary())) err = writeHeader(buffer, header) if err != nil { return nil, err } // Write the start of our `multipart/mixed` body. _, err = fmt.Fprintf(buffer, "--%s%s", mixedw.Boundary(), crlf) if err != nil { return nil, err } // Does the message have a body? if m.Body != "" || m.HTMLBody != "" { // Nested multipart writer for our `multipart/alternative` body. altw := multipart.NewWriter(buffer) header = textproto.MIMEHeader{} header.Add("Content-Type", fmt.Sprintf("multipart/alternative;%s boundary=%s", crlf, altw.Boundary())) err := writeHeader(buffer, header) if err != nil { return nil, err } if m.Body != "" { 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") partw, err := altw.CreatePart(header) if err != nil { return nil, err } bodyBytes := []byte(m.Body) //encoder := NewBase64MimeEncoder(partw) encoder := qprintable.NewEncoder(qprintable.DetectEncoding(m.Body), partw) _, err = encoder.Write(bodyBytes) if err != nil { return nil, err } err = encoder.Close() if err != nil { return nil, err } } if m.HTMLBody != "" { 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") partw, err := altw.CreatePart(header) if err != nil { return nil, err } htmlBodyBytes := []byte(m.HTMLBody) encoder := NewBase64MimeEncoder(partw) //encoder := qprintable.NewEncoder(qprintable.DetectEncoding(m.HTMLBody), partw) _, err = encoder.Write(htmlBodyBytes) if err != nil { return nil, err } err = encoder.Close() if err != nil { return nil, err } } altw.Close() } if m.Attachments != nil && len(m.Attachments) > 0 { 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 }
func HandleGetLocal(w http.ResponseWriter, r *http.Request, db *DBClient) { viewerID, err := OptionalAuthenticate(w, r, db) if err != nil { WriteErrorResp(w, err.Error(), DatabaseError) return } body, err := ioutil.ReadAll(r.Body) if err != nil { WriteErrorResp(w, err.Error(), ServerError) return } var searchMsg LocalSearchMsg err = json.Unmarshal(body, &searchMsg) if err != nil { WriteErrorResp(w, err.Error(), JsonError) return } loc := Geotag{ Latitude: searchMsg.Latitude, Longitude: searchMsg.Longitude, } beaconList, err := db.GetLocal(loc, searchMsg.Radius) if err != nil { WriteErrorResp(w, err.Error(), DatabaseError) return } respMsg := LocalSearchRespMsg{} for _, post := range beaconList { username, err := db.GetUsername(post.PosterID) if err != nil { WriteErrorResp(w, err.Error(), DatabaseError) return } var hearted bool if viewerID >= 0 { hearted, err = db.HasHearted(post.ID, uint64(viewerID)) if err != nil { WriteErrorResp(w, err.Error(), DatabaseError) return } } else { hearted = false } commentCount, err := db.GetCommentCount(post.ID) if err != nil { WriteErrorResp(w, err.Error(), DatabaseError) return } nextPost := RespThumbnailMsg{ SubmitBeaconMsg: SubmitBeaconMsg{ SubmitPostMsg: SubmitPostMsg{ Id: post.ID, Poster: post.PosterID, Text: post.Description, }, LocationMsg: LocationMsg{ Latitude: post.Location.Latitude, Longitude: post.Location.Longitude, }, }, RespPostMsg: RespPostMsg{ Hearts: post.Hearts, Time: FormatTime(post.Time), Username: username, Hearted: hearted, }, CommentCount: commentCount, } respMsg.Beacons = append(respMsg.Beacons, nextPost) } respJson, err := json.Marshal(respMsg) if err != nil { WriteErrorResp(w, err.Error(), ServerError) return } 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) for _, post := range beaconList { 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(post.Thumbnail) } partWriter.Close() w.Header().Add("Content-Type", partWriter.FormDataContentType()) w.Write(respBody.Bytes()) }
func (c *Client) UploadJSON(parent, filename string, payload []byte) (*Asset, error) { c.Log(DebugTrace, "[TRC] UploadJSON %v %v", filename, len(payload)) t, err := c.ts.Token() if err != nil { return nil, err } url := contentURL c.Log(DebugURL, "[URL] %v", url) // create body j := NodeJSON{ Name: filename, Kind: AssetFile, Parents: []string{parent}, } jj, err := json.Marshal(j) if err != nil { return nil, err } // metadata body := new(bytes.Buffer) writer := multipart.NewWriter(body) mh := textproto.MIMEHeader{} mh.Add("Content-Disposition", `form-data; name="metadata"`) mh.Add("Content-Type", "application/json") part, err := writer.CreatePart(mh) if err != nil { return nil, err } part.Write(jj) // content mh = textproto.MIMEHeader{} mh.Add("Content-Disposition", `form-data; name="content"; filename="`+ filename+`"`) mh.Add("Content-Type", http.DetectContentType(payload)) part, err = writer.CreatePart(mh) if err != nil { return nil, err } part.Write(payload) // flush writer.Close() // create http request req, err := http.NewRequest("POST", url, body) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+t.AccessToken) req.Header.Add("Content-Type", "multipart/form-data; boundary="+ writer.Boundary()) // dump body if c.GetMask()&DebugBody == DebugBody { x, _ := httputil.DumpRequestOut(req, true) c.Log(DebugBody, "BDY: %s", x) } // execute request clt := &http.Client{} res, err := clt.Do(req) if err != nil { return nil, err } defer res.Body.Close() c.Log(DebugHTTP, "[HTP] %v", res.Status) // obtain body rbody, err := ioutil.ReadAll(res.Body) if err != nil { return nil, err } c.Log(DebugBody, "[BDY] %v", string(rbody)) switch res.StatusCode { case http.StatusCreated: // success default: return nil, NewCombinedError(res.StatusCode, res.Status, rbody) } var asset Asset err = json.Unmarshal(rbody, &asset) if err != nil { return nil, err } return &asset, nil }
// Bytes gets the encoded MIME message. func (m *Message) Bytes() ([]byte, error) { var buffer = &bytes.Buffer{} header := textproto.MIMEHeader{} // Require To, Cc, or Bcc var hasTo = m.To != nil && len(m.To) > 0 var hasCc = m.Cc != nil && len(m.Cc) > 0 var hasBcc = m.Bcc != nil && len(m.Bcc) > 0 if !hasTo && !hasCc && !hasBcc { return nil, ErrMissingRecipient } else { if hasTo { toAddrs, err := getAddressListString(m.To) if err != nil { return nil, err } header.Add("To", toAddrs) } if hasCc { ccAddrs, err := getAddressListString(m.Cc) if err != nil { return nil, err } header.Add("Cc", ccAddrs) } } // Require From address if m.From == "" { return nil, ErrMissingFromAddress } else { parsedAddy, err := mail.ParseAddress(m.From) if err != nil { return nil, err } header.Add("From", parsedAddy.String()) } // Optional ReplyTo if m.ReplyTo != "" { parsedAddy, err := mail.ParseAddress(m.ReplyTo) if err != nil { return nil, err } header.Add("Reply-To", parsedAddy.String()) } // Optional Subject if m.Subject != "" { header.Add("Subject", qEncodeAndWrap(m.Subject, 9 /* len("Subject: ") */)) } // Top level multipart writer for our `multipart/mixed` body. mixedw := multipart.NewWriter(buffer) var err error header.Add("MIME-Version", "1.0") header.Add("Content-Type", fmt.Sprintf("multipart/mixed;%s boundary=%s", crlf, mixedw.Boundary())) err = writeHeader(buffer, header) if err != nil { return nil, err } // Write the start of our `multipart/mixed` body. _, err = fmt.Fprintf(buffer, "--%s%s", mixedw.Boundary(), crlf) if err != nil { return nil, err } // Does the message have a body? if m.Body != "" || m.HTMLBody != "" { // Nested multipart writer for our `multipart/alternative` body. altw := multipart.NewWriter(buffer) header = textproto.MIMEHeader{} header.Add("Content-Type", fmt.Sprintf("multipart/alternative;%s boundary=%s", crlf, altw.Boundary())) err := writeHeader(buffer, header) if err != nil { return nil, err } if m.Body != "" { 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") partw, err := altw.CreatePart(header) if err != nil { return nil, err } bodyBytes := []byte(m.Body) encoder := NewBase64MimeEncoder(partw) //encoder := qprintable.NewEncoder(qprintable.DetectEncoding(m.Body), partw) _, err = encoder.Write(bodyBytes) if err != nil { return nil, err } err = encoder.Close() if err != nil { return nil, err } } if m.HTMLBody != "" { 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") partw, err := altw.CreatePart(header) if err != nil { return nil, err } htmlBodyBytes := []byte(m.HTMLBody) encoder := NewBase64MimeEncoder(partw) //encoder := qprintable.NewEncoder(qprintable.DetectEncoding(m.HTMLBody), partw) _, err = encoder.Write(htmlBodyBytes) if err != nil { return nil, err } err = encoder.Close() if err != nil { return nil, err } } altw.Close() } if m.Attachments != nil && len(m.Attachments) > 0 { 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 } 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 }
func postStatementWithFile(t *testing.T, mart *martini.ClassicMartini, stmt, id string) (contentSHA2sum string) { // construct content sha2 := sha256.New() content := bytes.NewBuffer(nil) // write content fmt.Fprintln(io.MultiWriter(content, sha2), "example content text") contentSHA2sum = fmt.Sprintf("%x", sha2.Sum(nil)) // update statement var statement map[string]interface{} json.Unmarshal([]byte(stmt), &statement) statement["id"] = id statement["attachments"] = []map[string]interface{}{ { "usageType": "http://example.com/attachment-usage/test", "display": map[string]interface{}{ "en-US": "A test attachment", }, "description": map[string]interface{}{ "en-US": "A test attachment (description)", }, "contentType": "text/plain; charset=ascii", "length": content.Len(), "sha2": contentSHA2sum, }, } ustmt, _ := json.Marshal(statement) // // create multipart/form-data var header textproto.MIMEHeader buffer := bytes.NewBuffer(nil) encoder := multipart.NewWriter(buffer) // json field header = make(textproto.MIMEHeader) header.Add("Content-Type", "application/json") jsonfield, err := encoder.CreatePart(header) if err != nil { t.Fatal(err) } fmt.Fprintln(jsonfield, string(ustmt)) // text (content) field header = make(textproto.MIMEHeader) header.Add("Content-Type", "text/plain") header.Add("Content-Transfer-Encoding", "binary") header.Add("X-Experience-API-Hash", contentSHA2sum) textfield, err := encoder.CreatePart(header) if err != nil { t.Fatal(err) } io.Copy(textfield, content) // finish writing encoder.Close() resp := httptest.NewRecorder() req, _ := http.NewRequest("POST", "/test/test/statements", buffer) req.Header.Add("Content-Type", "multipart/mixed; boundary="+encoder.Boundary()) req.Header.Add("X-Experience-API-Version", "1.0.2") mart.ServeHTTP(resp, req) if got, expected := resp.Code, http.StatusOK; got != expected { r, _ := ioutil.ReadAll(resp.Body) t.Fatalf("Expected %v response code from post single statement with file; got %d, %v", expected, got, string(r)) } return }