コード例 #1
0
ファイル: api.go プロジェクト: espebra/filebin
func AdminCounters(w http.ResponseWriter, r *http.Request, cfg config.Configuration, ctx model.Context) {
	w.Header().Set("Vary", "Content-Type")
	w.Header().Set("Cache-Control", "s-maxage=0, max-age=0")
	var status = 200

	event := ctx.Events.New(ctx.RemoteAddr, []string{"admin", "counters"}, "", "")
	event.Update(r.Header.Get("user-agent"), 0)
	defer event.Done()

	stats := ctx.Metrics.GetStats()

	type Out struct {
		Counters       map[string]int64
		Uptime         time.Duration
		UptimeReadable string
		Now            time.Time
	}

	data := Out{
		Counters:       stats,
		Uptime:         ctx.Metrics.Uptime(),
		UptimeReadable: humanize.Time(ctx.Metrics.StartTime()),
		Now:            time.Now().UTC(),
	}

	if r.Header.Get("Accept") == "application/json" {
		w.Header().Set("Content-Type", "application/json")
		output.JSONresponse(w, status, data, ctx)
	} else {
		output.HTMLresponse(w, "counters", status, data, ctx)
	}
	return
}
コード例 #2
0
ファイル: api.go プロジェクト: espebra/filebin
func FetchBin(w http.ResponseWriter, r *http.Request, cfg config.Configuration, ctx model.Context) {
	w.Header().Set("Cache-Control", "s-maxage=15")
	w.Header().Set("Vary", "Content-Type")

	var status = 200

	params := mux.Vars(r)
	bin := params["bin"]
	if err := verifyBin(bin); err != nil {
		http.Error(w, "Invalid bin", 400)
		return
	}

	event := ctx.Events.New(ctx.RemoteAddr, []string{"bin", "view"}, bin, "")
	defer event.Done()

	var err error

	b, err := ctx.Backend.GetBinMetaData(bin)
	if err != nil {
		if ctx.Backend.BinExists(bin) {
			ctx.Log.Println(err)
			event.Update(err.Error(), 2)
			http.Error(w, "Internal Server Error", http.StatusInternalServerError)
			return
		} else {
			// This bin does not exist (but can be created)
			event.Update("Bin does not exist", 1)
			status = 404
			b = ctx.Backend.NewBin(bin)
		}
	}

	if b.Expired {
		http.Error(w, "This bin expired "+b.ExpiresReadable+".", 410)
		return
	}

	ctx.Metrics.Incr("total-view-bin")
	ctx.Metrics.Incr("bin-view bin=" + bin + " referer=" + r.Header.Get("Referer"))

	if r.Header.Get("Accept") == "application/json" {
		w.Header().Set("Content-Type", "application/json")
		output.JSONresponse(w, status, b, ctx)
		return
	} else {
		if len(b.Files) == 0 {
			output.HTMLresponse(w, "newbin", status, b, ctx)
		} else {
			output.HTMLresponse(w, "viewbin", status, b, ctx)
		}
		return
	}
}
コード例 #3
0
ファイル: api.go プロジェクト: espebra/filebin
func AdminEvents(w http.ResponseWriter, r *http.Request, cfg config.Configuration, ctx model.Context) {
	w.Header().Set("Vary", "Content-Type")
	w.Header().Set("Cache-Control", "s-maxage=0, max-age=0")
	var status = 200

	event := ctx.Events.New(ctx.RemoteAddr, []string{"admin", "events"}, "", "")
	event.Update(r.Header.Get("user-agent"), 0)
	defer event.Done()

	//u, err := url.Parse(r.RequestURI)
	//if err != nil {
	//	ctx.Log.Println(err)
	//}

	//queryParams, err := url.ParseQuery(u.RawQuery)
	//if err != nil {
	//	ctx.Log.Println(err)
	//}

	//filter := metrics.Event{
	//	Bin:        queryParams.Get("bin"),
	//	Category:   queryParams.Get("category"),
	//	Filename:   queryParams.Get("filename"),
	//	RemoteAddr: queryParams.Get("remoteaddr"),
	//	URL:        queryParams.Get("url"),
	//}

	type Out struct {
		Events         []events.Event
		Uptime         time.Duration
		UptimeReadable string
		Now            time.Time
	}

	data := Out{
		Events:         ctx.Events.GetAllEvents(0, 10000),
		Uptime:         ctx.Metrics.Uptime(),
		UptimeReadable: humanize.Time(ctx.Metrics.StartTime()),
		Now:            time.Now().UTC(),
	}

	if r.Header.Get("Accept") == "application/json" {
		w.Header().Set("Content-Type", "application/json")
		output.JSONresponse(w, status, data, ctx)
	} else {
		output.HTMLresponse(w, "events", status, data, ctx)
	}
	return
}
コード例 #4
0
ファイル: api.go プロジェクト: jmcarbo/filebin
func ViewIndex(w http.ResponseWriter, r *http.Request, cfg config.Configuration, ctx model.Context) {
	t := model.Tag{}
	tag := randomString(cfg.DefaultTagLength)
	err := t.SetTag(tag)
	if err != nil {
		ctx.Log.Println(err)
		http.Error(w, "Internal Server Error", 500)
		return
	}
	ctx.Log.Println("Tag generated: " + t.Tag)

	headers := make(map[string]string)
	headers["Cache-Control"] = "max-age=0"
	headers["Location"] = ctx.Baseurl + "/" + t.Tag
	var status = 302
	output.JSONresponse(w, status, headers, t, ctx)
}
コード例 #5
0
ファイル: api.go プロジェクト: espebra/filebin
func AdminBins(w http.ResponseWriter, r *http.Request, cfg config.Configuration, ctx model.Context) {
	w.Header().Set("Vary", "Content-Type")
	w.Header().Set("Cache-Control", "s-maxage=0, max-age=0")
	var status = 200

	event := ctx.Events.New(ctx.RemoteAddr, []string{"admin", "bins"}, "", "")
	event.Update(r.Header.Get("user-agent"), 0)
	defer event.Done()

	//event := metrics.Event{
	//	Category:   "admin-login",
	//	RemoteAddr: ctx.RemoteAddr,
	//	Text:       r.Header.Get("user-agent"),
	//	URL:        r.RequestURI,
	//}
	//ctx.Metrics.AddEvent(event)

	bins := ctx.Backend.GetBinsMetaData()

	type Out struct {
		Bins           []fs.Bin
		BinsReadable   string
		Uptime         time.Duration
		UptimeReadable string
		Now            time.Time
	}

	data := Out{
		Bins:           bins,
		BinsReadable:   humanize.Comma(int64(len(bins))),
		Uptime:         ctx.Metrics.Uptime(),
		UptimeReadable: humanize.Time(ctx.Metrics.StartTime()),
		Now:            time.Now().UTC(),
	}

	if r.Header.Get("Accept") == "application/json" {
		w.Header().Set("Content-Type", "application/json")
		output.JSONresponse(w, status, data, ctx)
	} else {
		output.HTMLresponse(w, "bins", status, data, ctx)
	}
	return
}
コード例 #6
0
ファイル: api.go プロジェクト: espebra/filebin
func NewBin(w http.ResponseWriter, r *http.Request, cfg config.Configuration, ctx model.Context) {
	w.Header().Set("Cache-Control", "s-maxage=0, max-age=0")
	w.Header().Set("Vary", "Content-Type")

	// XXX: Should ensure that the bin does not exist from before.
	bin := randomString(cfg.DefaultBinLength)
	b := ctx.Backend.NewBin(bin)

	ctx.Metrics.Incr("total-new-bin")

	var status = 200

	if r.Header.Get("Accept") == "application/json" {
		w.Header().Set("Content-Type", "application/json")
		output.JSONresponse(w, status, b, ctx)
	} else {
		output.HTMLresponse(w, "newbin", status, b, ctx)
	}
	return
}
コード例 #7
0
ファイル: api.go プロジェクト: jmcarbo/filebin
func Upload(w http.ResponseWriter, r *http.Request, cfg config.Configuration, ctx model.Context) {
	var err error
	f := model.File{}
	f.RemoteAddr = r.RemoteAddr
	f.UserAgent = r.Header.Get("User-Agent")

	// Extract the tag from the request
	if r.Header.Get("tag") == "" {
		tag := randomString(cfg.DefaultTagLength)
		err = f.SetTag(tag)
		ctx.Log.Println("Tag generated: " + f.Tag)
	} else {
		tag := r.Header.Get("tag")
		err = f.SetTag(tag)
		ctx.Log.Println("Tag specified: " + tag)
	}
	if err != nil {
		ctx.Log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	f.SetTagDir(cfg.Filedir)
	ctx.Log.Println("Tag directory: " + f.TagDir)

	// Write the request body to a temporary file
	err = f.WriteTempfile(r.Body, cfg.Tempdir)
	if err != nil {
		ctx.Log.Println("Unable to write tempfile: ", err)

		// Clean up by removing the tempfile
		f.ClearTemp()

		http.Error(w, "Internal Server Error", http.StatusInternalServerError)
		return
	}
	ctx.Log.Println("Tempfile: " + f.Tempfile)
	ctx.Log.Println("Tempfile size: " + strconv.FormatInt(f.Bytes, 10) + " bytes")

	// Do not accept files that are 0 bytes
	if f.Bytes == 0 {
		ctx.Log.Println("Empty files are not allowed. Aborting.")

		// Clean up by removing the tempfile
		f.ClearTemp()

		http.Error(w, "No content. The file size must be more than "+
			"0 bytes.", http.StatusBadRequest)
		return
	}

	// Calculate and verify the checksum
	checksum := r.Header.Get("content-sha256")
	if checksum != "" {
		ctx.Log.Println("Checksum specified: " + checksum)
	}
	err = f.VerifySHA256(checksum)
	ctx.Log.Println("Checksum calculated: " + f.Checksum)
	if err != nil {
		ctx.Log.Println("The specified checksum did not match")
		http.Error(w, "Checksum did not match", http.StatusConflict)
		return
	}

	// Trigger new tag
	t := model.Tag{}
	t.SetTag(f.Tag)
	t.SetTagDir(cfg.Filedir)
	if !t.TagDirExists() {
		if cfg.TriggerNewTag != "" {
			ctx.Log.Println("Executing trigger: New tag")
			triggerNewTagHandler(cfg.TriggerNewTag, f.Tag)
		}
	}

	// Create the tag directory if it does not exist
	err = f.EnsureTagDirectoryExists()
	if err != nil {
		ctx.Log.Println("Unable to create tag directory: ", f.TagDir)
		http.Error(w, "Internal Server Error", http.StatusInternalServerError)
		return
	}

	t.CalculateExpiration(cfg.Expiration)
	expired, err := t.IsExpired(cfg.Expiration)
	if err != nil {
		ctx.Log.Println(err)
		http.Error(w, "Internal server error", 500)
		return
	}
	if expired {
		ctx.Log.Println("The tag has expired. Aborting.")
		http.Error(w, "This tag has expired.", 410)
		return
	}

	// Extract the filename from the request
	fname := r.Header.Get("filename")
	if fname == "" {
		ctx.Log.Println("Filename generated: " + f.Checksum)
		f.SetFilename(f.Checksum)
	} else {
		ctx.Log.Println("Filename specified: " + fname)
		err = f.SetFilename(fname)
		if err != nil {
			ctx.Log.Println(err)
			http.Error(w, "Invalid filename specified. It contains illegal characters or is too short.",
				http.StatusBadRequest)
			return
		}
	}

	if fname != f.Filename {
		ctx.Log.Println("Filename sanitized: " + f.Filename)
	}

	err = f.DetectMIME()
	if err != nil {
		ctx.Log.Println("Unable to detect MIME: ", err)
	} else {
		ctx.Log.Println("MIME detected: " + f.MIME)
	}

	ctx.Log.Println("Media type: " + f.MediaType())
	if f.MediaType() == "image" {
		err = f.ParseExif()
		if err != nil {
			ctx.Log.Println(err)
		}

		if exif.IsCriticalError(err) == false {
			err = f.ExtractDateTime()
			if err != nil {
				ctx.Log.Println(err)
			}
		}

		// iOS devices provide only one filename even when uploading
		// multiple images. Providing some workaround for this below.
		// XXX: Refactoring needed.
		if isWorkaroundNeeded(f.UserAgent) && !f.DateTime.IsZero() {
			var fname string
			dt := f.DateTime.Format("060102-150405")

			// List of filenames to modify
			if f.Filename == "image.jpeg" {
				fname = "img-" + dt + ".jpeg"
			}
			if f.Filename == "image.gif" {
				fname = "img-" + dt + ".gif"
			}
			if f.Filename == "image.png" {
				fname = "img-" + dt + ".png"
			}

			if fname != "" {
				ctx.Log.Println("Filename workaround triggered")
				ctx.Log.Println("Filename modified: " + fname)
				err = f.SetFilename(fname)
				if err != nil {
					ctx.Log.Println(err)
				}
			}
		}

		//err = f.GenerateThumbnail()
		//if err != nil {
		//	ctx.Log.Println(err)
		//}

		extra := make(map[string]string)
		if !f.DateTime.IsZero() {
			extra["DateTime"] = f.DateTime.String()
		}
		f.Extra = extra
	}

	// Promote file from tempdir to the published tagdir
	f.Publish()

	// Clean up by removing the tempfile
	f.ClearTemp()

	err = f.StatInfo()
	if err != nil {
		ctx.Log.Println(err)
		http.Error(w, "Internal Server Error", 500)
		return
	}

	f.GenerateLinks(cfg.Baseurl)
	f.CreatedAt = time.Now().UTC()
	//f.ExpiresAt = time.Now().UTC().Add(24 * 7 * 4 * time.Hour)

	if cfg.TriggerUploadedFile != "" {
		ctx.Log.Println("Executing trigger: Uploaded file")
		triggerUploadedFileHandler(cfg.TriggerUploadedFile, f.Tag, f.Filename)
	}

	ctx.WorkQueue <- f

	headers := make(map[string]string)
	headers["Content-Type"] = "application/json"

	var status = 201
	output.JSONresponse(w, status, headers, f, ctx)
}
コード例 #8
0
ファイル: api.go プロジェクト: jmcarbo/filebin
func FetchTag(w http.ResponseWriter, r *http.Request, cfg config.Configuration, ctx model.Context) {
	var err error
	params := mux.Vars(r)
	t := model.Tag{}
	err = t.SetTag(params["tag"])
	if err != nil {
		ctx.Log.Println(err)
		http.Error(w, "Invalid tag", 400)
		return
	}

	t.SetTagDir(cfg.Filedir)
	t.CalculateExpiration(cfg.Expiration)
	if t.TagDirExists() {
		expired, err := t.IsExpired(cfg.Expiration)
		if err != nil {
			ctx.Log.Println(err)
			http.Error(w, "Internal server error", 500)
			return
		}
		if expired {
			ctx.Log.Println("Expired: " + t.ExpirationReadable)
			http.Error(w, "This tag has expired.", 410)
			return
		}

		err = t.StatInfo()
		if err != nil {
			ctx.Log.Println(err)
			http.Error(w, "Internal Server Error", 500)
			return
		}

		err = t.List(cfg.Baseurl)
		if err != nil {
			ctx.Log.Println(err)
			http.Error(w, "Error reading the tag contents.", 404)
			return
		}
	}

	//t.GenerateLinks(cfg.Baseurl)

	headers := make(map[string]string)
	headers["Cache-Control"] = "max-age=1"

	var status = 200

	if r.Header.Get("Content-Type") == "application/zip" || r.FormValue("o") == "zip" {
		headers["Content-Type"] = "application/zip"

		// Generate a map of paths to add to the zip response
		var paths []string
		for _, f := range t.Files {
			path := filepath.Join(f.TagDir, f.Filename)
			paths = append(paths, path)
		}
		output.ZIPresponse(w, status, t.Tag, headers, paths, ctx)
		return
	}

	if r.Header.Get("Content-Type") == "application/json" {
		headers["Content-Type"] = "application/json"
		output.JSONresponse(w, status, headers, t, ctx)
		return
	} else {
		output.HTMLresponse(w, "viewtag", status, headers, t, ctx)
		return
	}
}
コード例 #9
0
ファイル: api.go プロジェクト: jmcarbo/filebin
func DeleteFile(w http.ResponseWriter, r *http.Request, cfg config.Configuration, ctx model.Context) {
	var err error
	params := mux.Vars(r)
	f := model.File{}
	f.SetFilename(params["filename"])
	if err != nil {
		ctx.Log.Println(err)
		http.Error(w, "Invalid filename specified. It contains illegal characters or is too short.", 400)
		return
	}
	err = f.SetTag(params["tag"])
	if err != nil {
		ctx.Log.Println(err)
		http.Error(w, "Invalid tag specified. It contains illegal characters or is too short.", 400)
		return
	}
	f.SetTagDir(cfg.Filedir)

	if f.Exists() == false {
		ctx.Log.Println("The file does not exist.")
		http.Error(w, "File Not Found", 404)
		return
	}

	t := model.Tag{}
	t.SetTag(f.Tag)
	t.SetTagDir(cfg.Filedir)
	t.CalculateExpiration(cfg.Expiration)
	expired, err := t.IsExpired(cfg.Expiration)
	if err != nil {
		ctx.Log.Println(err)
		http.Error(w, "Internal server error", 500)
		return
	}
	if expired {
		ctx.Log.Println("Expired: " + t.ExpirationReadable)
		http.Error(w, "This tag has expired.", 410)
		return
	}

	f.GenerateLinks(cfg.Baseurl)
	err = f.DetectMIME()
	if err != nil {
		ctx.Log.Println("Unable to detect MIME: ", err)
	}

	err = f.StatInfo()
	if err != nil {
		ctx.Log.Println(err)
		http.Error(w, "Internal Server Error", 500)
		return
	}

	err = f.Remove()
	if err != nil {
		ctx.Log.Println("Unable to remove file: ", err)
		http.Error(w, "Internal Server Error", 500)
		return
	}

	headers := make(map[string]string)
	headers["Content-Type"] = "application/json"

	var status = 200
	output.JSONresponse(w, status, headers, f, ctx)
	return
}
コード例 #10
0
ファイル: api.go プロジェクト: espebra/filebin
func AdminDashboard(w http.ResponseWriter, r *http.Request, cfg config.Configuration, ctx model.Context) {
	w.Header().Set("Vary", "Content-Type")
	w.Header().Set("Cache-Control", "s-maxage=0, max-age=0")
	var status = 200

	eventsInProgress := ctx.Events.GetEventsInProgress(0, 0)

	event := ctx.Events.New(ctx.RemoteAddr, []string{"admin", "dashboard"}, "", "")
	event.Update(r.Header.Get("user-agent"), 0)
	defer event.Done()

	logins := ctx.Events.GetEventsByTags([]string{"admin"}, 0, 3)

	bins := ctx.Backend.GetBinsMetaData()
	stats := ctx.Metrics.GetStats()

	// Detect time limit for showing recent events
	limitTime := time.Now().UTC().Add(-48 * time.Hour)
	if len(logins) >= 2 {
		limitTime = logins[1].StartTime()
	}

	var recentUploads []events.Event
	uploads := ctx.Events.GetEventsByTags([]string{"upload"}, 0, 0)
	for _, f := range uploads {
		if f.StartTime().After(limitTime) {
			if f.IsDone() && f.Status() == 0 {
				recentUploads = append(recentUploads, f)
			}
		}
	}

	var recentEvents []events.Event
	allEvents := ctx.Events.GetAllEvents(1, 0)
	for _, e := range allEvents {
		if e.StartTime().After(limitTime) {
			recentEvents = append(recentEvents, e)
		}
	}

	type Out struct {
		Bins             []fs.Bin
		BinsReadable     string
		Events           []events.Event
		EventsInProgress []events.Event
		Uploads          []events.Event
		Files            int
		FilesReadable    string
		Bytes            int64
		BytesReadable    string
		Stats            map[string]int64
		Logins           []events.Event
		Uptime           time.Duration
		UptimeReadable   string
		Now              time.Time
	}

	var files int
	var bytes int64
	for _, b := range bins {
		files = files + len(b.Files)
		bytes = bytes + b.Bytes
	}

	data := Out{
		Bins:             bins,
		Events:           recentEvents,
		EventsInProgress: eventsInProgress,
		Uploads:          recentUploads,
		Files:            files,
		Bytes:            bytes,
		BytesReadable:    humanize.Bytes(uint64(bytes)),
		BinsReadable:     humanize.Comma(int64(len(bins))),
		FilesReadable:    humanize.Comma(int64(files)),
		Stats:            stats,
		Logins:           logins,
		Uptime:           ctx.Metrics.Uptime(),
		UptimeReadable:   humanize.Time(ctx.Metrics.StartTime()),
		Now:              time.Now().UTC(),
	}

	if r.Header.Get("Accept") == "application/json" {
		w.Header().Set("Content-Type", "application/json")
		output.JSONresponse(w, status, data, ctx)
	} else {
		output.HTMLresponse(w, "dashboard", status, data, ctx)
	}
	return
}
コード例 #11
0
ファイル: api.go プロジェクト: espebra/filebin
func FetchFile(w http.ResponseWriter, r *http.Request, cfg config.Configuration, ctx model.Context) {
	w.Header().Set("Cache-Control", "s-maxage=30")
	w.Header().Set("Vary", "Content-Type")

	// Query parameters
	u, err := url.Parse(r.RequestURI)
	if err != nil {
		ctx.Log.Println(err)
	}

	queryParams, err := url.ParseQuery(u.RawQuery)
	if err != nil {
		ctx.Log.Println(err)
	}

	params := mux.Vars(r)
	bin := params["bin"]
	if err := verifyBin(bin); err != nil {
		http.Error(w, "Invalid bin", 400)
		return
	}

	b, err := ctx.Backend.GetBinMetaData(bin)
	if err != nil {
		ctx.Log.Println(err)
		http.Error(w, "Not found", 404)
		return
	}

	if b.Expired {
		http.Error(w, "This bin expired "+b.ExpiresReadable+".", 410)
		return
	}

	filename := params["filename"]
	if err := verifyFilename(filename); err != nil {
		http.Error(w, "Invalid filename", 400)
		return
	}

	f, err := ctx.Backend.GetFileMetaData(bin, filename)
	if err != nil {
		ctx.Log.Println(err)
		http.Error(w, "Not found", 404)
		return
	}

	if r.Header.Get("Accept") == "application/json" {
		w.Header().Set("Content-Type", "application/json")
		output.JSONresponse(w, 200, f, ctx)
		return
	}

	width, _ := strconv.Atoi(queryParams.Get("width"))
	height, _ := strconv.Atoi(queryParams.Get("height"))
	if (width > 0) || (height > 0) {
		fp, err := ctx.Backend.GetThumbnail(bin, filename, width, height)
		if err != nil {
			ctx.Log.Println(err)
			http.Error(w, "Image not found", 404)
			return
		}
		ctx.Metrics.Incr("total-thumbnails-viewed")
		http.ServeContent(w, r, f.Filename, f.CreatedAt, fp)
		return
	}

	event := ctx.Events.New(ctx.RemoteAddr, []string{"file", "download"}, bin, filename)
	defer event.Done()
	event.Update(humanize.Bytes(uint64(f.Bytes)), 0)

	ctx.Metrics.Incr("total-file-download")
	ctx.Metrics.Incr("current-file-download")
	defer ctx.Metrics.Decr("current-file-download")
	ctx.Metrics.Incr("file-download bin=" + bin + " filename=" + filename + " referer=" + r.Header.Get("Referer"))

	fp, err := ctx.Backend.GetFile(bin, filename)
	if err != nil {
		ctx.Log.Println(err)
		event.Update(err.Error(), 2)
		http.Error(w, "Not found", 404)
		return
	}

	w.Header().Set("Content-SHA256", f.Checksum)

	if cfg.TriggerDownloadFile != "" {
		ctx.Log.Println("Executing trigger: Download file")
		triggerDownloadFileHandler(cfg.TriggerDownloadFile, bin, filename)
	}

	http.ServeContent(w, r, f.Filename, f.CreatedAt, fp)
}
コード例 #12
0
ファイル: api.go プロジェクト: espebra/filebin
func Upload(w http.ResponseWriter, r *http.Request, cfg config.Configuration, ctx model.Context) {
	r.Close = true

	bin := r.Header.Get("bin")
	if err := verifyBin(bin); err != nil {
		http.Error(w, "Invalid bin", 400)
		return
	}

	b, err := ctx.Backend.GetBinMetaData(bin)
	if err == nil {
		if b.Expired {
			http.Error(w, "This bin expired "+b.ExpiresReadable+".", 410)
			return
		}
	}

	filename := sanitizeFilename(r.Header.Get("filename"))
	if err := verifyFilename(filename); err != nil {
		http.Error(w, "Invalid filename", 400)
		return
	}

	ctx.Metrics.Incr("current-upload")
	defer ctx.Metrics.Decr("current-upload")

	event := ctx.Events.New(ctx.RemoteAddr, []string{"file", "upload"}, bin, filename)
	defer event.Done()

	if i, err := strconv.Atoi(r.Header.Get("content-length")); err == nil {
		event.Update("Size: "+humanize.Bytes(uint64(i)), 0)
	}

	if ctx.Backend.BinExists(bin) == false {
		if cfg.TriggerNewBin != "" {
			ctx.Log.Println("Executing trigger: New bin")
			triggerNewBinHandler(cfg.TriggerNewBin, bin)
		}
	}

	f, err := ctx.Backend.UploadFile(bin, filename, r.Body)
	if err != nil {
		ctx.Log.Println(err)
		http.Error(w, "Internal Server Error", http.StatusInternalServerError)
		event.Update(err.Error(), 2)
		return
	}

	ctx.Metrics.Incr("total-upload")

	if cfg.TriggerUploadFile != "" {
		ctx.Log.Println("Executing trigger: Uploaded file")
		triggerUploadFileHandler(cfg.TriggerUploadFile, f.Bin, f.Filename)
	}

	// Purging any old content
	if cfg.CacheInvalidation {
		for _, l := range f.Links {
			if err := shared.PurgeURL(l.Href, ctx.Log); err != nil {
				ctx.Log.Println(err)
			}
		}
	}

	j := model.Job{}
	j.Filename = f.Filename
	j.Bin = f.Bin
	j.Log = ctx.Log
	j.Cfg = &cfg
	ctx.WorkQueue <- j

	w.Header().Set("Content-Type", "application/json")

	var status = 201
	output.JSONresponse(w, status, f, ctx)
}