func savePart(mp *i18nmail.MailPart, ctx *Context) (fn string, err error) { var n int head := make([]byte, 512) tfh, e := temp.NewReadSeeker(mp.Body) if e != nil { err = errgo.Notef(e, "savePart") return } if n, err = io.ReadFull(mp.Body, head); err != nil && err != io.EOF { err = errgo.Notef(err, "cannot read head of %d: %s", mp.Seq) return } fn = mp.Header.Get("X-FileName") if fn == "" { fn = ".eml" } head = head[:n] if mp.ContentType == "" && mp.Parent == nil { mp.ContentType = "message/rfc822" } mp.ContentType = FixContentType(head, mp.ContentType, fn) ctx = prepareContext(ctx, "") fn = filepath.Join(ctx.Dir, fmt.Sprintf("%02d!%03d.%s.%s", mp.Level, mp.Seq, strings.Replace(mp.ContentType, "/", "--", -1), fn)) mp.Body = tfh return fn, 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 }
// MailToPdfFiles converts email to PDF files // all mail part goes through all filter in Filters, in reverse order (last first) func MailToPdfFiles(r io.Reader, ctx *Context) (files []ArchFileItem, err error) { hsh := sha1.New() br, e := temp.NewReadSeeker(io.TeeReader(r, hsh)) if e != nil { err = errgo.Notef(e, "MailToPdfFiles") return } defer func() { _ = br.Close() }() ctx = prepareContext(ctx, base64.URLEncoding.EncodeToString(hsh.Sum(nil))) r = br files = make([]ArchFileItem, 0, 16) errs := make([]string, 0, 16) resultch := make(chan ArchFileItem) rawch := make(chan i18nmail.MailPart) errch := make(chan error) go SlurpMail(rawch, errch, r) // SlurpMail sends read parts to partch partch := SetupFilters(rawch, resultch, errch, ctx) // convert parts var workWg sync.WaitGroup worker := func() { defer workWg.Done() for mp := range partch { if err := convertPart(mp, resultch, ctx); err != nil { errch <- err } } } for i := 0; i < Concurrency; i++ { workWg.Add(1) go worker() } go func() { workWg.Wait() close(resultch) }() // collect results and errors Collect: for { var ok bool var item ArchFileItem select { case item, ok = <-resultch: if ok { files = append(files, item) } else { //closed close(errch) break Collect } case err = <-errch: if err != nil { errs = append(errs, err.Error()) } } } if err != nil && err != io.EOF { errs = append(errs, "error reading parts: "+err.Error()) } if len(errs) > 0 { err = errgo.Newf(strings.Join(errs, "\n")) } return files, err }
func uploadHandler(params martini.Params, uf UploadForm) (int, string) { if !hasPermission(params["apikey"], UploadPermission) { return http.StatusUnauthorized, uploadError("API key invalid or missing") } if uf.PhotoUpload == nil { return http.StatusBadRequest, uploadError("missing image field") } // Check signature only when API key is used // Note: when no API key is passed in but required for uploads, the above // hasPermission check should fail if params["apikey"] != "" { uploadTime := time.Unix(uf.Timestamp, 0) delta := time.Since(uploadTime).Minutes() if delta < 0 || delta > 5 { return http.StatusBadRequest, uploadError("invalid timestamp") } queryParams := make(map[string]string) queryParams["timestamp"] = strconv.FormatInt(uf.Timestamp, 10) secret, err := getSecretForKey(params["apikey"]) if err != nil { return http.StatusBadRequest, uploadError("authorization error") } if !isValidSignature(uf.Signature, secret, queryParams) { return http.StatusBadRequest, uploadError("invalid signature") } } file, err := uf.PhotoUpload.Open() if err != nil { return http.StatusBadRequest, uploadError(err.Error()) } reader, err := temp.NewReadSeeker(file) if err != nil { return http.StatusBadRequest, uploadError(err.Error()) } c, _, err := image.DecodeConfig(reader) if err != nil { return http.StatusBadRequest, uploadError(err.Error()) } reader.Seek(0, 0) pixels := c.Width * c.Height if pixels > Config.uploadMaxPixels { return http.StatusBadRequest, uploadError(fmt.Sprintf("too many pixels: %d, allowed: %d", pixels, Config.uploadMaxPixels)) } limit := io.LimitReader(reader, int64(Config.uploadMaxFileSize+1)) data, err := ioutil.ReadAll(limit) if err != nil { return http.StatusBadRequest, uploadError(err.Error()) } if len(data) > Config.uploadMaxFileSize { return http.StatusBadRequest, uploadError("max file size exceeded") } img, format, err := image.Decode(bytes.NewReader(data)) if err != nil { return http.StatusBadRequest, uploadError(err.Error()) } defer file.Close() // Not a big fan of .jpeg file extensions now := time.Now() randomInt := rand.Intn(1000) baseImagePath := fmt.Sprintf("%d-%d.%s", now.Unix(), randomInt, strings.Replace(format, "jpeg", "jpg", 1)) log.Printf("Uploading %s", baseImagePath) // Eager transformations eagerlyTransform := func() { if len(Config.eagerTransformations) > 0 { for _, transformation := range Config.eagerTransformations { imgNew := transformCropAndResize(img, &transformation) fullImagePath, _ := transformation.createFilePath(baseImagePath) addToCache(fullImagePath, imgNew, format) } } } if Config.asyncUploads { go func() { _, err := saveImage(img, format, baseImagePath) if err != nil { log.Println("Error saving image:", err) return } go eagerlyTransform() }() } else { _, err := saveImage(img, format, baseImagePath) if err != nil { return http.StatusInternalServerError, uploadError("error saving image: " + err.Error()) } go eagerlyTransform() } return http.StatusOK, uploadSuccess(baseImagePath) }