func TestGetObject(t *testing.T) { header := http.Header{} textproto.MIMEHeader(header).Add("Content-Type", "image/jpeg") textproto.MIMEHeader(header).Add("Content-ID", "123456") textproto.MIMEHeader(header).Add("Object-ID", "1") textproto.MIMEHeader(header).Add("Preferred", "1") textproto.MIMEHeader(header).Add("UID", "1a234234234") textproto.MIMEHeader(header).Add("Content-Description", "Outhouse") textproto.MIMEHeader(header).Add("Content-Sub-Description", "The urinal") textproto.MIMEHeader(header).Add("Location", "http://www.simpleboundary.com/image-5.jpg") var body string = `<binary data 1>` reader := ioutil.NopCloser(bytes.NewReader([]byte(body))) quit := make(chan struct{}) defer close(quit) results := parseGetObjectResult(quit, header, reader) result := <-results o := result.Object testutils.Ok(t, result.Err) testutils.Equals(t, true, o.Preferred) testutils.Equals(t, "image/jpeg", o.ContentType) testutils.Equals(t, "123456", o.ContentId) testutils.Equals(t, 1, o.ObjectId) testutils.Equals(t, "1a234234234", o.Uid) testutils.Equals(t, "Outhouse", o.Description) testutils.Equals(t, "The urinal", o.SubDescription) testutils.Equals(t, "<binary data 1>", string(o.Blob)) testutils.Equals(t, "http://www.simpleboundary.com/image-5.jpg", o.Location) testutils.Equals(t, false, o.RetsError) }
func (p *Multipart) AddText(mediaType string, txt string) error { if p.isClosed { return ErrPartClosed } p.header = textproto.MIMEHeader(map[string][]string{ "Content-Type": {mediaType}, "Content-Transfer-Encoding": {"quoted-printable"}, }) w, err := p.writer.CreatePart(p.header) if err != nil { return err } encoder := qp.NewWriter(w) _, err = io.WriteString(encoder, txt) if err != nil { return err } encoder.Close() fmt.Fprintf(w, crlf) fmt.Fprintf(w, crlf) return nil }
// AddText applies quoted-printable encoding to the content of r before writing // the encoded result in a new sub-part with media MIME type set to mediaType. // // Specifying the charset in the mediaType string is recommended // ("plain/text; charset=utf-8"). func (p *Multipart) AddText(mediaType string, r io.Reader) error { if p.isClosed { return ErrPartClosed } p.header = textproto.MIMEHeader(map[string][]string{ "Content-Type": {mediaType}, "Content-Transfer-Encoding": {"quoted-printable"}, }) w, err := p.writer.CreatePart(p.header) if err != nil { return err } reader := bufio.NewReader(r) encoder := qp.NewWriter(w) defer encoder.Close() buffer := make([]byte, maxLineLen) for { read, err := reader.Read(buffer[:]) if err != nil { if err != io.EOF { return err } break } encoder.Write(buffer[:read]) } fmt.Fprintf(w, crlf) fmt.Fprintf(w, crlf) return nil }
func getHeader(i map[string][]string, header string) string { h, ok := textproto.MIMEHeader(i)[textproto.CanonicalMIMEHeaderKey(header)] if ok { return h[0] } return "" }
// ForEach ... func (r *GetObjectResponse) ForEach(result GetObjectResult) error { resp := r.response defer resp.Body.Close() mediaType, params, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) if err != nil { return err } // its not multipart, just leave if !strings.HasPrefix(mediaType, "multipart/") { return result(NewObjectFromStream(textproto.MIMEHeader(resp.Header), resp.Body)) } // its multipart, need to break it up partsReader := multipart.NewReader(resp.Body, params["boundary"]) for { part, err := partsReader.NextPart() switch { case err == io.EOF: return nil case err != nil: return err } err = result(NewObjectFromStream(part.Header, part)) if err != nil { return err } } // return nil }
// getOneRequestFile reads the first file from the request (if multipart/), // or returns the body if not func getOneRequestFile(r *http.Request) (reqFile, error) { f := reqFile{ReadCloser: r.Body} contentType := r.Header.Get("Content-Type") logger.Info("msg", "readRequestOneFile", "ct", contentType) if !strings.HasPrefix(contentType, "multipart/") { f.FileHeader.Header = textproto.MIMEHeader(r.Header) return f, nil } defer r.Body.Close() if err := r.ParseMultipartForm(1 << 20); err != nil { return f, errors.New("error parsing request as multipart-form: " + err.Error()) } if r.MultipartForm == nil || len(r.MultipartForm.File) == 0 { return f, errors.New("no files?") } for _, fileHeaders := range r.MultipartForm.File { for _, fileHeader := range fileHeaders { var err error if f.ReadCloser, err = fileHeader.Open(); err != nil { return f, fmt.Errorf("error opening part %q: %s", fileHeader.Filename, err) } f.FileHeader = *fileHeader return f, nil } } return reqFile{}, nil }
func (cb *couchBackend) mkArticle(ar Article) *nntp.Article { url := fmt.Sprintf("%s/%s/article", cb.db.DBURL(), cleanupId(ar.MsgId, true)) return &nntp.Article{ Header: textproto.MIMEHeader(ar.Headers), Body: &lazyOpener{url, nil, nil}, Bytes: ar.Bytes, Lines: ar.Lines, } }
// Text sets the text content of the email. func (e *Email) Text(text string) { if nil == e.alternative { panic("e.alternative IS NIL") } out, _ := e.alternative.CreatePart(textproto.MIMEHeader(map[string][]string{ "Content-Type": []string{"text/plain; charset=utf-8"}, })) fmt.Fprintln(out, text) }
// AddAttachment encodes the content of r in base64 and writes it as an // attachment of type attachType in this part. // // filename is the file name that will be suggested by the mail user agent to a // user who would like to download the attachment. It's also the value to which // the Content-ID header will be set. A name with an extension such as // "report.docx" or "photo.jpg" is recommended. RFC 5987 is not supported, so // the charset is restricted to ASCII characters. // // mediaType indicates the content type of the attachment. If an empty string is // passed, mime.TypeByExtension will first be called to deduce a value from the // extension of filemame before defaulting to "application/octet-stream". // // In the following example, the media MIME type will be set to "image/png" // based on the ".png" extension of the filename "gopher.png": // part.AddAttachment(Inline, "gopher.png", "", image) func (p *Multipart) AddAttachment(attachType AttachmentType, filename, mediaType string, r io.Reader) (err error) { if p.isClosed { return ErrPartClosed } // Default Content-Type value if mediaType == "" && filename != "" { mediaType = mime.TypeByExtension(filepath.Ext(filename)) } if mediaType == "" { mediaType = "application/octet-stream" } header := textproto.MIMEHeader(map[string][]string{ "Content-Type": {mediaType}, "Content-ID": {fmt.Sprintf("<%s>", filename)}, "Content-Location": {fmt.Sprintf("%s", filename)}, "Content-Transfer-Encoding": {"base64"}, "Content-Disposition": {fmt.Sprintf("%s;\r\n\tfilename=%s;", attachType, filename)}, }) w, err := p.writer.CreatePart(header) if err != nil { return err } encoder := base64.NewEncoder(base64.StdEncoding, w) data := bufio.NewReader(r) buffer := make([]byte, int(math.Ceil(maxLineLen/4)*3)) for { read, err := io.ReadAtLeast(data, buffer[:], len(buffer)) if err != nil { if err == io.EOF { break } else if err != io.ErrUnexpectedEOF { return err } } if _, err := encoder.Write(buffer[:read]); err != nil { return err } if read == len(buffer) { fmt.Fprintf(w, crlf) } } encoder.Close() fmt.Fprintf(w, crlf) return nil }
// GetHeader returns the undecoded value of header if found. To access the // raw (potentially encoded) value of header, use the Message.Header. func (m *Message) GetHeader(header string) string { e := textproto.MIMEHeader(m.Header).Get(header) if e == "" { return "" } dec := new(qp.WordDecoder) decoded, err := dec.DecodeHeader(e) if err != nil { return "" } return decoded }
func parseGetObjectResult(quit <-chan struct{}, header http.Header, body io.ReadCloser) <-chan GetObjectResult { data := make(chan GetObjectResult) go func() { defer body.Close() defer close(data) select { case data <- parseHeadersAndStream(textproto.MIMEHeader(header), body): case <-quit: return } }() return data }
func (m *Message) GetMultipleHeaderValues(header string) (values []string) { headers := textproto.MIMEHeader(m.Header) list := headers[header] for _, v := range list { dec := new(qp.WordDecoder) decoded, err := dec.DecodeHeader(v) if err != nil { continue } values = append(values, decoded) } return values }
func newResponseParameters(shared bool, cacheRequest *cacheRequest, resource *Resource) (int, int64, http.Header, io.ReadCloser, error) { age, err := resource.Age() if err != nil { return int(http.StatusInternalServerError), -1, nil, nil, err } contentLength := resource.ContentLength() statusCode := resource.Status() headers := make(http.Header) for key, mimeheaders := range resource.Header() { for _, header := range mimeheaders { textproto.MIMEHeader(headers).Add(key, header) } } // http://httpwg.github.io/specs/rfc7234.html#warn.113 if age > (time.Hour*24) && resource.HeuristicFreshness() > (time.Hour*24) { textproto.MIMEHeader(headers).Add("Warning", `113 - "Heuristic Expiration"`) } // http://httpwg.github.io/specs/rfc7234.html#warn.110 freshness, err := freshness(shared, resource, cacheRequest) if err != nil || freshness <= 0 { textproto.MIMEHeader(headers).Add("Warning", `110 - "Response is Stale"`) } debugf("resource is %s old, updating age from %s", age.String(), headers.Get("Age")) textproto.MIMEHeader(headers).Set("Age", fmt.Sprintf("%.f", math.Floor(age.Seconds()))) textproto.MIMEHeader(headers).Set("Via", resource.Via()) body := resource return statusCode, contentLength, headers, body, nil }
// Walk over the parts of the email, calling todo on every part. // The part.Body given to todo is reused, so read if you want to use it! // // By default this is recursive, except dontDescend is true. func Walk(part MailPart, todo TodoFunc, dontDescend bool) error { br, e := temp.NewReadSeeker(part.Body) if e != nil { return e } defer func() { _ = br.Close() }() msg, hsh, e := ReadAndHashMessage(br) if e != nil { return errgo.Notef(e, "WalkMail") } ct, params, decoder, e := getCT(msg.Header) logger.Info("msg", "Walk message", "hsh", hsh, "headers", msg.Header) if e != nil { return errgo.Notef(e, "WalkMail") } if ct == "" { ct = "message/rfc822" } child := MailPart{ContentType: ct, MediaType: params, Header: textproto.MIMEHeader(msg.Header), Body: msg.Body, Parent: &part, Level: part.Level + 1, Seq: nextSeqInt()} child.Header.Add("X-Hash", hsh) if child.Header.Get(HashKeyName) == "" { child.Header.Add(HashKeyName, hsh) } logger.Debug("msg", "message", "sequence", child.Seq, "content-type", ct, "params", params) if strings.HasPrefix(ct, "multipart/") { return WalkMultipart(child, todo, dontDescend) } if !dontDescend && strings.HasPrefix(ct, "message/") { //mail if decoder != nil { child.Body = decoder(child.Body) } if e = Walk(child, todo, dontDescend); e != nil { return errgo.Notef(e, "WalkMail descending") } return nil } //simple if decoder != nil { child.Body = decoder(child.Body) } if e = todo(child); e != nil { return errgo.Notef(e, "todo") } return nil }
func TestGetObject(t *testing.T) { header := http.Header{} textproto.MIMEHeader(header).Add("Content-Type", "image/jpeg") textproto.MIMEHeader(header).Add("Content-ID", "123456") textproto.MIMEHeader(header).Add("Object-ID", "1") textproto.MIMEHeader(header).Add("Preferred", "1") textproto.MIMEHeader(header).Add("UID", "1a234234234") textproto.MIMEHeader(header).Add("Content-Description", "Outhouse") textproto.MIMEHeader(header).Add("Content-Sub-Description", "The urinal") textproto.MIMEHeader(header).Add("Location", "http://www.simpleboundary.com/image-5.jpg") var body = `<binary data 1>` response := GetObjectResponse{ response: &http.Response{ Header: header, Body: ioutil.NopCloser(strings.NewReader(body)), }, } defer response.Close() var objects []*Object err := response.ForEach(func(o *Object, err error) error { objects = append(objects, o) return nil }) testutils.Ok(t, err) testutils.Equals(t, 1, len(objects)) o := objects[0] testutils.Equals(t, true, o.Preferred) testutils.Equals(t, "image/jpeg", o.ContentType) testutils.Equals(t, "123456", o.ContentID) testutils.Equals(t, 1, o.ObjectID) testutils.Equals(t, "1a234234234", o.UID) testutils.Equals(t, "Outhouse", o.Description) testutils.Equals(t, "The urinal", o.SubDescription) testutils.Equals(t, "<binary data 1>", string(o.Blob)) testutils.Equals(t, "http://www.simpleboundary.com/image-5.jpg", o.Location) testutils.Equals(t, false, o.RetsError) }
func (h Headers) Set(key, value string) { textproto.MIMEHeader(h).Set(key, value) }
func (h Headers) Add(key, value string) { textproto.MIMEHeader(h).Add(key, value) }
func ParsePO(r io.ReadSeeker) (*Catalog, error) { var magic uint32 if err := binary.Read(r, binary.LittleEndian, &magic); err != nil { return nil, err } var bo binary.ByteOrder if magic == leMagic { bo = binary.LittleEndian } else if magic == beMagic { bo = binary.BigEndian } else { return nil, ErrInvalidMagic } var version, stringCount, origOffset, transOffset uint32 if err := binary.Read(r, bo, &version); err != nil { return nil, err } if err := binary.Read(r, bo, &stringCount); err != nil { return nil, err } if err := binary.Read(r, bo, &origOffset); err != nil { return nil, err } if err := binary.Read(r, bo, &transOffset); err != nil { return nil, err } // TODO: Check version stringOffsets := make([]struct{ origStart, origLen, transStart, transLen int32 }, stringCount) if o, err := r.Seek(int64(origOffset), 0); err != nil { return nil, err } else if o != int64(origOffset) { return nil, ErrTruncated } for i := 0; i < int(stringCount); i++ { if err := binary.Read(r, bo, &stringOffsets[i].origLen); err != nil { return nil, err } if err := binary.Read(r, bo, &stringOffsets[i].origStart); err != nil { return nil, err } } if o, err := r.Seek(int64(transOffset), 0); err != nil { return nil, err } else if o != int64(transOffset) { return nil, ErrTruncated } for i := 0; i < int(stringCount); i++ { if err := binary.Read(r, bo, &stringOffsets[i].transLen); err != nil { return nil, err } if err := binary.Read(r, bo, &stringOffsets[i].transStart); err != nil { return nil, err } } catalog := Catalog{ Strings: make(map[string]*Translation, stringCount), PluralFormula: GermanicPluralFormula, } for _, so := range stringOffsets { if o, err := r.Seek(int64(so.origStart), 0); err != nil { return nil, err } else if o != int64(so.origStart) { return nil, ErrTruncated } origBytes := make([]byte, so.origLen) if _, err := r.Read(origBytes); err != nil { return nil, err } if o, err := r.Seek(int64(so.transStart), 0); err != nil { return nil, err } else if o != int64(so.transStart) { return nil, ErrTruncated } transBytes := make([]byte, so.transLen) if _, err := r.Read(transBytes); err != nil { return nil, err } if len(origBytes) == 0 { // Translation meta header header, err := textproto.NewReader( bufio.NewReader(bytes.NewReader(transBytes))).ReadMIMEHeader() if err != nil { catalog.Header = header } else { catalog.Header = textproto.MIMEHeader(make(map[string][]string)) } } else { origParts := strings.Split(string(origBytes), "\x00") transParts := strings.Split(string(transBytes), "\x00") if len(transParts) > 0 { t := &Translation{ Translation: transParts, } catalog.Strings[origParts[0]] = t if len(origParts) > 1 { t.Plural = origParts[1] } } } } return &catalog, nil }
func (h Header) Add(key, value string) { //都是相同的map结构 textproto.MIMEHeader(h).Add(key, value) //将header类型转换 }
// Get gets the first value associated with the given key. // If there are no values associated with the key, Get returns "". // To access multiple values of a key, access the map directly // with CanonicalHeaderKey. func (h Header) Get(key string) string { // 获得header对应的key return textproto.MIMEHeader(h).Get(key) }
// Set sets the header entries associated with key to // the single element value. It replaces any existing // values associated with key. func (h Header) Set(key, value string) { // 设置key, value textproto.MIMEHeader(h).Set(key, value) }
func (c *Client) UploadMedia(userID, albumID string, m *UploadMediaInfo, contentType string, contentLength int64, r io.Reader) (*Media, error) { if userID == "" { userID = "default" } if albumID == "" { albumID = "default" } const metaDataContentType = "application/atom+xml" var media *Media var err error var totalContentLength int64 pipeOut, pipeIn := io.Pipe() uri := fmt.Sprintf("%s/data/feed/api/user/%s/albumid/%s", c.BaseURL, userID, albumID) writer := multipart.NewWriter(pipeIn) wg := new(sync.WaitGroup) wg.Add(1) metaData, _ := xml.Marshal(m) totalContentLength += int64(3 + len(writer.Boundary())) // --boundary\n totalContentLength += int64(14 + len(metaDataContentType) + 1) // Content-Type: xxxx\n\n totalContentLength += int64(len(metaData) + 1) // {xml}\n totalContentLength += int64(3 + len(writer.Boundary())) // --boundary\n totalContentLength += int64(14 + len(contentType) + 1) // Content-Type: xxxx\n\n totalContentLength += int64(len(metaData) + 1) // {binary}\n totalContentLength += int64(3 + len(writer.Boundary())) // --boundary\n go func() { defer wg.Done() var resp *http.Response var req *http.Request req, err = http.NewRequest("POST", uri, pipeOut) if err != nil { return } req.Header.Set("Content-Type", fmt.Sprintf("multipart/related; boundary=%q", writer.Boundary())) req.Header.Set("Content-Length", fmt.Sprintf("%d", totalContentLength)) resp, err = c.client.Do(req) if err != nil { return } defer resp.Body.Close() if resp.StatusCode != 201 { buff, _ := ioutil.ReadAll(resp.Body) err = fmt.Errorf("API Error: %s", string(buff)) return } media, err = parsePhotoFeed(resp.Body) }() metaDataPart, err := writer.CreatePart(textproto.MIMEHeader(map[string][]string{ "Content-Type": []string{metaDataContentType}, })) if err != nil { return nil, err } metaDataPart.Write(metaData) binaryPart, err := writer.CreatePart(textproto.MIMEHeader(map[string][]string{ "Content-Type": []string{contentType}, })) if _, err = io.Copy(binaryPart, r); err != nil { return nil, err } writer.Close() pipeIn.Close() wg.Wait() return media, err }
// SetHeader adds header to the list of headers and sets it to quoted-printable // encoded value. func (m *Message) SetHeader(header, value string) { textproto.MIMEHeader(m.Header).Set(header, value) }
// AddHeader appends the header value to the list of values for this header key func (m *Message) AddHeader(header, value string) { textproto.MIMEHeader(m.Header).Add(header, value) }
func (server vermServer) FileUploader(w http.ResponseWriter, req *http.Request, replicating bool) (*fileUpload, error) { // deal with '/..' etc. path := path.Clean(req.URL.Path) location := "" if replicating { location = path lastSlash := strings.LastIndex(path, "/") if lastSlash < 4 { return nil, &WrongLocationError{path} } path = path[0 : lastSlash-3] } // don't allow uploads to the root directory itself, which would be unmanageable if len(path) <= 1 { path = DefaultDirectoryIfNotGivenByClient } // make a tempfile in the requested (or default, as above) directory directory := server.RootDataDir + path err := os.MkdirAll(directory, DirectoryPermission) if err != nil { return nil, err } var tempFile *os.File tempFile, err = ioutil.TempFile(directory, "_upload") if err != nil { return nil, err } // if the upload is a raw post, the input stream is the request body var input io.Reader = req.Body // but if the upload is a browser form, the input stream needs multipart decoding contentType := mediaTypeOrDefault(textproto.MIMEHeader(req.Header)) if contentType == "multipart/form-data" { file, mpheader, mperr := req.FormFile(UploadedFieldFieldForMultipart) if mperr != nil { return nil, mperr } input = file contentType = mediaTypeOrDefault(mpheader.Header) } // determine the appropriate extension from the content type extension := mimeext.ExtensionByType(contentType) // if the file is both gzip-encoded and is actually a gzip file itself, strip the redundant encoding storageEncoding := req.Header.Get("Content-Encoding") if extension == ".gz" && storageEncoding != "" { input, err = EncodingDecoder(storageEncoding, input) if err != nil { return nil, err } storageEncoding = "" } // as we read from the stream, copy it into the tempfile - potentially in encoded format (except for the above case) input = io.TeeReader(input, tempFile) // but uncompress the stream before feeding it to the hasher input, err = EncodingDecoder(storageEncoding, input) if err != nil { return nil, err } // in addition to handling gzip content-encoding, if an actual .gz file is uploaded, // we need to decompressed it and hash its contents rather than the raw file itself; // otherwise there would be an ambiguity between application/octet-stream files with // gzip on-disk compression and application/gzip files with no compression, and they // would appear to have different hashes, which would break replication if extension == ".gz" { input, err = gzip.NewReader(input) if err != nil { return nil, err } } return &fileUpload{ replicating: replicating, root: server.RootDataDir, path: path, location: location, contentType: contentType, extension: extension, encoding: storageEncoding, input: input, hasher: sha256.New(), tempFile: tempFile, }, nil }
// ExtractingFilter is a filter for the mail pipeline which extracts archives func ExtractingFilter(inch <-chan i18nmail.MailPart, outch chan<- i18nmail.MailPart, files chan<- ArchFileItem, errch chan<- error, ctx *Context) { defer func() { logger.Debug("msg", "ExtractingFilter closes", "outch", outch) close(outch) }() allIn := make(chan i18nmail.MailPart, 1024) var wg sync.WaitGroup // for waiting all input to finish go func() { for part := range inch { wg.Add(1) allIn <- part } wg.Wait() close(allIn) }() for part := range allIn { var ( makeReader func(io.Reader) (uncompr.Lister, error) zr uncompr.Lister err error ) body := io.Reader(part.Body) if part.ContentType == "application/x-ole-storage" { r, err := NewOLEStorageReader(body) if err != nil { goto Error } child := part.Spawn() child.ContentType, child.Body = "message/rfc822", r child.Header = textproto.MIMEHeader(map[string][]string{ "X-FileName": []string{ safeFn(part.Header.Get("X-FileName")+".eml", true), }}) wg.Add(1) allIn <- child wg.Done() continue } switch part.ContentType { case "application/zip": makeReader = uncompr.NewZipLister case "application/rar": makeReader = uncompr.NewRarLister //case "application/tar": makeReader = UnTar default: goto Skip } zr, err = makeReader(body) if err != nil { goto Error } for i, z := range zr.List() { rc, err := z.Open() if err != nil { logger.Warn("msg", "open zip element", "i", i, "error", err) continue } chunk, err := ioutil.ReadAll(rc) _ = rc.Close() if err != nil { logger.Warn("msg", "read zip element", "i", i, "error", err) continue } child := part.Spawn() child.ContentType = FixContentType(chunk, "application/octet-stream", z.Name()) child.Body = bytes.NewBuffer(chunk) child.Header = textproto.MIMEHeader(make(map[string][]string, 1)) child.Header.Add("X-FileName", safeFn(z.Name(), true)) wg.Add(1) allIn <- child } wg.Done() continue Error: logger.Error("msg", "ExtractingFilter", "error", err) if err != nil { errch <- err } Skip: wg.Done() outch <- part } }
// Del deletes the values associated with the key. func (h Header) Del(key, value string) { textproto.MIMEHeader(h).Del(key) }
func (h Headers) Get(key string) string { return textproto.MIMEHeader(h).Get(key) }
func (h Headers) Del(key string) { textproto.MIMEHeader(h).Del(key) }
func NewEntity(msg *mail.Message, dumpDir string) (*Entity, error) { shared := &sharedData{ dumpDir: dumpDir, } return newEntity(textproto.MIMEHeader(msg.Header), msg.Body, shared) }