Example #1
0
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
}
Example #2
0
// 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
}
Example #3
0
// 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
}
Example #4
0
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)
}