aboutsummaryrefslogtreecommitdiffstats
path: root/library/blueimp_upload/server/gae-go/app/main.go
diff options
context:
space:
mode:
Diffstat (limited to 'library/blueimp_upload/server/gae-go/app/main.go')
-rw-r--r--library/blueimp_upload/server/gae-go/app/main.go361
1 files changed, 0 insertions, 361 deletions
diff --git a/library/blueimp_upload/server/gae-go/app/main.go b/library/blueimp_upload/server/gae-go/app/main.go
deleted file mode 100644
index a92d128c0..000000000
--- a/library/blueimp_upload/server/gae-go/app/main.go
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
- * jQuery File Upload Plugin GAE Go Example
- * https://github.com/blueimp/jQuery-File-Upload
- *
- * Copyright 2011, Sebastian Tschan
- * https://blueimp.net
- *
- * Licensed under the MIT license:
- * https://opensource.org/licenses/MIT
- */
-
-package app
-
-import (
- "bufio"
- "bytes"
- "encoding/json"
- "fmt"
- "github.com/disintegration/gift"
- "golang.org/x/net/context"
- "google.golang.org/appengine"
- "google.golang.org/appengine/memcache"
- "hash/crc32"
- "image"
- "image/gif"
- "image/jpeg"
- "image/png"
- "io"
- "log"
- "mime/multipart"
- "net/http"
- "net/url"
- "path/filepath"
- "regexp"
- "strings"
-)
-
-const (
- WEBSITE = "https://blueimp.github.io/jQuery-File-Upload/"
- MIN_FILE_SIZE = 1 // bytes
- // Max file size is memcache limit (1MB) minus key size minus overhead:
- MAX_FILE_SIZE = 999000 // bytes
- IMAGE_TYPES = "image/(gif|p?jpeg|(x-)?png)"
- ACCEPT_FILE_TYPES = IMAGE_TYPES
- THUMB_MAX_WIDTH = 80
- THUMB_MAX_HEIGHT = 80
- EXPIRATION_TIME = 300 // seconds
- // If empty, only allow redirects to the referer protocol+host.
- // Set to a regexp string for custom pattern matching:
- REDIRECT_ALLOW_TARGET = ""
-)
-
-var (
- imageTypes = regexp.MustCompile(IMAGE_TYPES)
- acceptFileTypes = regexp.MustCompile(ACCEPT_FILE_TYPES)
- thumbSuffix = "." + fmt.Sprint(THUMB_MAX_WIDTH) + "x" +
- fmt.Sprint(THUMB_MAX_HEIGHT)
-)
-
-func escape(s string) string {
- return strings.Replace(url.QueryEscape(s), "+", "%20", -1)
-}
-
-func extractKey(r *http.Request) string {
- // Use RequestURI instead of r.URL.Path, as we need the encoded form:
- path := strings.Split(r.RequestURI, "?")[0]
- // Also adjust double encoded slashes:
- return strings.Replace(path[1:], "%252F", "%2F", -1)
-}
-
-func check(err error) {
- if err != nil {
- panic(err)
- }
-}
-
-type FileInfo struct {
- Key string `json:"-"`
- ThumbnailKey string `json:"-"`
- Url string `json:"url,omitempty"`
- ThumbnailUrl string `json:"thumbnailUrl,omitempty"`
- Name string `json:"name"`
- Type string `json:"type"`
- Size int64 `json:"size"`
- Error string `json:"error,omitempty"`
- DeleteUrl string `json:"deleteUrl,omitempty"`
- DeleteType string `json:"deleteType,omitempty"`
-}
-
-func (fi *FileInfo) ValidateType() (valid bool) {
- if acceptFileTypes.MatchString(fi.Type) {
- return true
- }
- fi.Error = "Filetype not allowed"
- return false
-}
-
-func (fi *FileInfo) ValidateSize() (valid bool) {
- if fi.Size < MIN_FILE_SIZE {
- fi.Error = "File is too small"
- } else if fi.Size > MAX_FILE_SIZE {
- fi.Error = "File is too big"
- } else {
- return true
- }
- return false
-}
-
-func (fi *FileInfo) CreateUrls(r *http.Request, c context.Context) {
- u := &url.URL{
- Scheme: r.URL.Scheme,
- Host: appengine.DefaultVersionHostname(c),
- Path: "/",
- }
- uString := u.String()
- fi.Url = uString + fi.Key
- fi.DeleteUrl = fi.Url
- fi.DeleteType = "DELETE"
- if fi.ThumbnailKey != "" {
- fi.ThumbnailUrl = uString + fi.ThumbnailKey
- }
-}
-
-func (fi *FileInfo) SetKey(checksum uint32) {
- fi.Key = escape(string(fi.Type)) + "/" +
- escape(fmt.Sprint(checksum)) + "/" +
- escape(string(fi.Name))
-}
-
-func (fi *FileInfo) createThumb(buffer *bytes.Buffer, c context.Context) {
- if imageTypes.MatchString(fi.Type) {
- src, _, err := image.Decode(bytes.NewReader(buffer.Bytes()))
- check(err)
- filter := gift.New(gift.ResizeToFit(
- THUMB_MAX_WIDTH,
- THUMB_MAX_HEIGHT,
- gift.LanczosResampling,
- ))
- dst := image.NewNRGBA(filter.Bounds(src.Bounds()))
- filter.Draw(dst, src)
- buffer.Reset()
- bWriter := bufio.NewWriter(buffer)
- switch fi.Type {
- case "image/jpeg", "image/pjpeg":
- err = jpeg.Encode(bWriter, dst, nil)
- case "image/gif":
- err = gif.Encode(bWriter, dst, nil)
- default:
- err = png.Encode(bWriter, dst)
- }
- check(err)
- bWriter.Flush()
- thumbnailKey := fi.Key + thumbSuffix + filepath.Ext(fi.Name)
- item := &memcache.Item{
- Key: thumbnailKey,
- Value: buffer.Bytes(),
- }
- err = memcache.Set(c, item)
- check(err)
- fi.ThumbnailKey = thumbnailKey
- }
-}
-
-func handleUpload(r *http.Request, p *multipart.Part) (fi *FileInfo) {
- fi = &FileInfo{
- Name: p.FileName(),
- Type: p.Header.Get("Content-Type"),
- }
- if !fi.ValidateType() {
- return
- }
- defer func() {
- if rec := recover(); rec != nil {
- log.Println(rec)
- fi.Error = rec.(error).Error()
- }
- }()
- var buffer bytes.Buffer
- hash := crc32.NewIEEE()
- mw := io.MultiWriter(&buffer, hash)
- lr := &io.LimitedReader{R: p, N: MAX_FILE_SIZE + 1}
- _, err := io.Copy(mw, lr)
- check(err)
- fi.Size = MAX_FILE_SIZE + 1 - lr.N
- if !fi.ValidateSize() {
- return
- }
- fi.SetKey(hash.Sum32())
- item := &memcache.Item{
- Key: fi.Key,
- Value: buffer.Bytes(),
- }
- context := appengine.NewContext(r)
- err = memcache.Set(context, item)
- check(err)
- fi.createThumb(&buffer, context)
- fi.CreateUrls(r, context)
- return
-}
-
-func getFormValue(p *multipart.Part) string {
- var b bytes.Buffer
- io.CopyN(&b, p, int64(1<<20)) // Copy max: 1 MiB
- return b.String()
-}
-
-func handleUploads(r *http.Request) (fileInfos []*FileInfo) {
- fileInfos = make([]*FileInfo, 0)
- mr, err := r.MultipartReader()
- check(err)
- r.Form, err = url.ParseQuery(r.URL.RawQuery)
- check(err)
- part, err := mr.NextPart()
- for err == nil {
- if name := part.FormName(); name != "" {
- if part.FileName() != "" {
- fileInfos = append(fileInfos, handleUpload(r, part))
- } else {
- r.Form[name] = append(r.Form[name], getFormValue(part))
- }
- }
- part, err = mr.NextPart()
- }
- return
-}
-
-func validateRedirect(r *http.Request, redirect string) bool {
- if redirect != "" {
- var redirectAllowTarget *regexp.Regexp
- if REDIRECT_ALLOW_TARGET != "" {
- redirectAllowTarget = regexp.MustCompile(REDIRECT_ALLOW_TARGET)
- } else {
- referer := r.Referer()
- if referer == "" {
- return false
- }
- refererUrl, err := url.Parse(referer)
- if err != nil {
- return false
- }
- redirectAllowTarget = regexp.MustCompile("^" + regexp.QuoteMeta(
- refererUrl.Scheme+"://"+refererUrl.Host+"/",
- ))
- }
- return redirectAllowTarget.MatchString(redirect)
- }
- return false
-}
-
-func get(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path == "/" {
- http.Redirect(w, r, WEBSITE, http.StatusFound)
- return
- }
- // Use RequestURI instead of r.URL.Path, as we need the encoded form:
- key := extractKey(r)
- parts := strings.Split(key, "/")
- if len(parts) == 3 {
- context := appengine.NewContext(r)
- item, err := memcache.Get(context, key)
- if err == nil {
- w.Header().Add("X-Content-Type-Options", "nosniff")
- contentType, _ := url.QueryUnescape(parts[0])
- if !imageTypes.MatchString(contentType) {
- contentType = "application/octet-stream"
- }
- w.Header().Add("Content-Type", contentType)
- w.Header().Add(
- "Cache-Control",
- fmt.Sprintf("public,max-age=%d", EXPIRATION_TIME),
- )
- w.Write(item.Value)
- return
- }
- }
- http.Error(w, "404 Not Found", http.StatusNotFound)
-}
-
-func post(w http.ResponseWriter, r *http.Request) {
- result := make(map[string][]*FileInfo, 1)
- result["files"] = handleUploads(r)
- b, err := json.Marshal(result)
- check(err)
- if redirect := r.FormValue("redirect"); validateRedirect(r, redirect) {
- if strings.Contains(redirect, "%s") {
- redirect = fmt.Sprintf(
- redirect,
- escape(string(b)),
- )
- }
- http.Redirect(w, r, redirect, http.StatusFound)
- return
- }
- w.Header().Set("Cache-Control", "no-cache")
- jsonType := "application/json"
- if strings.Index(r.Header.Get("Accept"), jsonType) != -1 {
- w.Header().Set("Content-Type", jsonType)
- }
- fmt.Fprintln(w, string(b))
-}
-
-func delete(w http.ResponseWriter, r *http.Request) {
- key := extractKey(r)
- parts := strings.Split(key, "/")
- if len(parts) == 3 {
- result := make(map[string]bool, 1)
- context := appengine.NewContext(r)
- err := memcache.Delete(context, key)
- if err == nil {
- result[key] = true
- contentType, _ := url.QueryUnescape(parts[0])
- if imageTypes.MatchString(contentType) {
- thumbnailKey := key + thumbSuffix + filepath.Ext(parts[2])
- err := memcache.Delete(context, thumbnailKey)
- if err == nil {
- result[thumbnailKey] = true
- }
- }
- }
- w.Header().Set("Content-Type", "application/json")
- b, err := json.Marshal(result)
- check(err)
- fmt.Fprintln(w, string(b))
- } else {
- http.Error(w, "405 Method not allowed", http.StatusMethodNotAllowed)
- }
-}
-
-func handle(w http.ResponseWriter, r *http.Request) {
- params, err := url.ParseQuery(r.URL.RawQuery)
- check(err)
- w.Header().Add("Access-Control-Allow-Origin", "*")
- w.Header().Add(
- "Access-Control-Allow-Methods",
- "OPTIONS, HEAD, GET, POST, DELETE",
- )
- w.Header().Add(
- "Access-Control-Allow-Headers",
- "Content-Type, Content-Range, Content-Disposition",
- )
- switch r.Method {
- case "OPTIONS", "HEAD":
- return
- case "GET":
- get(w, r)
- case "POST":
- if len(params["_method"]) > 0 && params["_method"][0] == "DELETE" {
- delete(w, r)
- } else {
- post(w, r)
- }
- case "DELETE":
- delete(w, r)
- default:
- http.Error(w, "501 Not Implemented", http.StatusNotImplemented)
- }
-}
-
-func init() {
- http.HandleFunc("/", handle)
-}