diff options
Diffstat (limited to 'mod/lightpics/vendors/jquery-file-upload/server')
18 files changed, 1603 insertions, 0 deletions
| diff --git a/mod/lightpics/vendors/jquery-file-upload/server/gae-go/app.yaml b/mod/lightpics/vendors/jquery-file-upload/server/gae-go/app.yaml new file mode 100644 index 000000000..2d09daa56 --- /dev/null +++ b/mod/lightpics/vendors/jquery-file-upload/server/gae-go/app.yaml @@ -0,0 +1,12 @@ +application: jquery-file-upload +version: 2 +runtime: go +api_version: go1 + +handlers: +- url: /(favicon\.ico|robots\.txt) +  static_files: static/\1 +  upload: static/(.*) +  expiration: '1d' +- url: /.* +  script: _go_app diff --git a/mod/lightpics/vendors/jquery-file-upload/server/gae-go/app/main.go b/mod/lightpics/vendors/jquery-file-upload/server/gae-go/app/main.go new file mode 100644 index 000000000..01dc2f204 --- /dev/null +++ b/mod/lightpics/vendors/jquery-file-upload/server/gae-go/app/main.go @@ -0,0 +1,361 @@ +/* + * jQuery File Upload Plugin GAE Go Example 2.0 + * https://github.com/blueimp/jQuery-File-Upload + * + * Copyright 2011, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +package app + +import ( +	"appengine" +	"appengine/blobstore" +	"appengine/memcache" +	"appengine/taskqueue" +	"bytes" +	"encoding/base64" +	"encoding/json" +	"fmt" +	"image" +	"image/png" +	"io" +	"log" +	"mime/multipart" +	"net/http" +	"net/url" +	"regexp" +	"resize" +	"strings" +	"time" +) + +import _ "image/gif" +import _ "image/jpeg" + +const ( +	WEBSITE              = "http://blueimp.github.com/jQuery-File-Upload/" +	MIN_FILE_SIZE        = 1       // bytes +	MAX_FILE_SIZE        = 5000000 // bytes +	IMAGE_TYPES          = "image/(gif|p?jpeg|(x-)?png)" +	ACCEPT_FILE_TYPES    = IMAGE_TYPES +	EXPIRATION_TIME      = 300 // seconds +	THUMBNAIL_MAX_WIDTH  = 80 +	THUMBNAIL_MAX_HEIGHT = THUMBNAIL_MAX_WIDTH +) + +var ( +	imageTypes      = regexp.MustCompile(IMAGE_TYPES) +	acceptFileTypes = regexp.MustCompile(ACCEPT_FILE_TYPES) +) + +type FileInfo struct { +	Key          appengine.BlobKey `json:"-"` +	Url          string            `json:"url,omitempty"` +	ThumbnailUrl string            `json:"thumbnail_url,omitempty"` +	Name         string            `json:"name"` +	Type         string            `json:"type"` +	Size         int64             `json:"size"` +	Error        string            `json:"error,omitempty"` +	DeleteUrl    string            `json:"delete_url,omitempty"` +	DeleteType   string            `json:"delete_type,omitempty"` +} + +func (fi *FileInfo) ValidateType() (valid bool) { +	if acceptFileTypes.MatchString(fi.Type) { +		return true +	} +	fi.Error = "acceptFileTypes" +	return false +} + +func (fi *FileInfo) ValidateSize() (valid bool) { +	if fi.Size < MIN_FILE_SIZE { +		fi.Error = "minFileSize" +	} else if fi.Size > MAX_FILE_SIZE { +		fi.Error = "maxFileSize" +	} else { +		return true +	} +	return false +} + +func (fi *FileInfo) CreateUrls(r *http.Request, c appengine.Context) { +	u := &url.URL{ +		Scheme: r.URL.Scheme, +		Host:   appengine.DefaultVersionHostname(c), +		Path:   "/", +	} +	uString := u.String() +	fi.Url = uString + escape(string(fi.Key)) + "/" + +		escape(string(fi.Name)) +	fi.DeleteUrl = fi.Url +	fi.DeleteType = "DELETE" +	if fi.ThumbnailUrl != "" && -1 == strings.Index( +		r.Header.Get("Accept"), +		"application/json", +	) { +		fi.ThumbnailUrl = uString + "thumbnails/" + +			escape(string(fi.Key)) +	} +} + +func (fi *FileInfo) CreateThumbnail(r io.Reader, c appengine.Context) (data []byte, err error) { +	defer func() { +		if rec := recover(); rec != nil { +			log.Println(rec) +			// 1x1 pixel transparent GIf, bas64 encoded: +			s := "R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" +			data, _ = base64.StdEncoding.DecodeString(s) +			fi.ThumbnailUrl = "data:image/gif;base64," + s +		} +		memcache.Add(c, &memcache.Item{ +			Key:        string(fi.Key), +			Value:      data, +			Expiration: EXPIRATION_TIME, +		}) +	}() +	img, _, err := image.Decode(r) +	check(err) +	if bounds := img.Bounds(); bounds.Dx() > THUMBNAIL_MAX_WIDTH || +		bounds.Dy() > THUMBNAIL_MAX_HEIGHT { +		w, h := THUMBNAIL_MAX_WIDTH, THUMBNAIL_MAX_HEIGHT +		if bounds.Dx() > bounds.Dy() { +			h = bounds.Dy() * h / bounds.Dx() +		} else { +			w = bounds.Dx() * w / bounds.Dy() +		} +		img = resize.Resize(img, img.Bounds(), w, h) +	} +	var b bytes.Buffer +	err = png.Encode(&b, img) +	check(err) +	data = b.Bytes() +	fi.ThumbnailUrl = "data:image/png;base64," + +		base64.StdEncoding.EncodeToString(data) +	return +} + +func check(err error) { +	if err != nil { +		panic(err) +	} +} + +func escape(s string) string { +	return strings.Replace(url.QueryEscape(s), "+", "%20", -1) +} + +func delayedDelete(c appengine.Context, fi *FileInfo) { +	if key := string(fi.Key); key != "" { +		task := &taskqueue.Task{ +			Path:   "/" + escape(key) + "/-", +			Method: "DELETE", +			Delay:  time.Duration(EXPIRATION_TIME) * time.Second, +		} +		taskqueue.Add(c, task, "") +	} +} + +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 b bytes.Buffer +	lr := &io.LimitedReader{R: p, N: MAX_FILE_SIZE + 1} +	context := appengine.NewContext(r) +	w, err := blobstore.Create(context, fi.Type) +	defer func() { +		w.Close() +		fi.Size = MAX_FILE_SIZE + 1 - lr.N +		fi.Key, err = w.Key() +		check(err) +		if !fi.ValidateSize() { +			err := blobstore.Delete(context, fi.Key) +			check(err) +			return +		} +		delayedDelete(context, fi) +		if b.Len() > 0 { +			fi.CreateThumbnail(&b, context) +		} +		fi.CreateUrls(r, context) +	}() +	check(err) +	var wr io.Writer = w +	if imageTypes.MatchString(fi.Type) { +		wr = io.MultiWriter(&b, w) +	} +	_, err = io.Copy(wr, lr) +	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 get(w http.ResponseWriter, r *http.Request) { +	if r.URL.Path == "/" { +		http.Redirect(w, r, WEBSITE, http.StatusFound) +		return +	} +	parts := strings.Split(r.URL.Path, "/") +	if len(parts) == 3 { +		if key := parts[1]; key != "" { +			blobKey := appengine.BlobKey(key) +			bi, err := blobstore.Stat(appengine.NewContext(r), blobKey) +			if err == nil { +				w.Header().Add( +					"Cache-Control", +					fmt.Sprintf("public,max-age=%d", EXPIRATION_TIME), +				) +				if imageTypes.MatchString(bi.ContentType) { +					w.Header().Add("X-Content-Type-Options", "nosniff") +				} else { +					w.Header().Add("Content-Type", "application/octet-stream") +					w.Header().Add( +						"Content-Disposition:", +						fmt.Sprintf("attachment; filename=%s;", parts[2]), +					) +				} +				blobstore.Send(w, appengine.BlobKey(key)) +				return +			} +		} +	} +	http.Error(w, "404 Not Found", http.StatusNotFound) +} + +func post(w http.ResponseWriter, r *http.Request) { +	b, err := json.Marshal(handleUploads(r)) +	check(err) +	if redirect := r.FormValue("redirect"); redirect != "" { +		http.Redirect(w, r, fmt.Sprintf( +			redirect, +			escape(string(b)), +		), http.StatusFound) +		return +	} +	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) { +	parts := strings.Split(r.URL.Path, "/") +	if len(parts) != 3 { +		return +	} +	if key := parts[1]; key != "" { +		c := appengine.NewContext(r) +		blobstore.Delete(c, appengine.BlobKey(key)) +		memcache.Delete(c, key) +	} +} + +func serveThumbnail(w http.ResponseWriter, r *http.Request) { +	parts := strings.Split(r.URL.Path, "/") +	if len(parts) == 3 { +		if key := parts[2]; key != "" { +			var data []byte +			c := appengine.NewContext(r) +			item, err := memcache.Get(c, key) +			if err == nil { +				data = item.Value +			} else { +				blobKey := appengine.BlobKey(key) +				if _, err = blobstore.Stat(c, blobKey); err == nil { +					fi := FileInfo{Key: blobKey} +					data, _ = fi.CreateThumbnail( +						blobstore.NewReader(c, blobKey), +						c, +					) +				} +			} +			if err == nil && len(data) > 3 { +				w.Header().Add( +					"Cache-Control", +					fmt.Sprintf("public,max-age=%d", EXPIRATION_TIME), +				) +				contentType := "image/png" +				if string(data[:3]) == "GIF" { +					contentType = "image/gif" +				} else if string(data[1:4]) != "PNG" { +					contentType = "image/jpeg" +				} +				w.Header().Set("Content-Type", contentType) +				fmt.Fprintln(w, string(data)) +				return +			} +		} +	} +	http.Error(w, "404 Not Found", http.StatusNotFound) +} + +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, PUT, DELETE", +	) +	switch r.Method { +	case "OPTIONS": +	case "HEAD": +	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) +	http.HandleFunc("/thumbnails/", serveThumbnail) +} diff --git a/mod/lightpics/vendors/jquery-file-upload/server/gae-go/resize/resize.go b/mod/lightpics/vendors/jquery-file-upload/server/gae-go/resize/resize.go new file mode 100644 index 000000000..dcb627870 --- /dev/null +++ b/mod/lightpics/vendors/jquery-file-upload/server/gae-go/resize/resize.go @@ -0,0 +1,247 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package resize + +import ( +	"image" +	"image/color" +) + +// Resize returns a scaled copy of the image slice r of m. +// The returned image has width w and height h. +func Resize(m image.Image, r image.Rectangle, w, h int) image.Image { +	if w < 0 || h < 0 { +		return nil +	} +	if w == 0 || h == 0 || r.Dx() <= 0 || r.Dy() <= 0 { +		return image.NewRGBA64(image.Rect(0, 0, w, h)) +	} +	switch m := m.(type) { +	case *image.RGBA: +		return resizeRGBA(m, r, w, h) +	case *image.YCbCr: +		if m, ok := resizeYCbCr(m, r, w, h); ok { +			return m +		} +	} +	ww, hh := uint64(w), uint64(h) +	dx, dy := uint64(r.Dx()), uint64(r.Dy()) +	// The scaling algorithm is to nearest-neighbor magnify the dx * dy source +	// to a (ww*dx) * (hh*dy) intermediate image and then minify the intermediate +	// image back down to a ww * hh destination with a simple box filter. +	// The intermediate image is implied, we do not physically allocate a slice +	// of length ww*dx*hh*dy. +	// For example, consider a 4*3 source image. Label its pixels from a-l: +	//	abcd +	//	efgh +	//	ijkl +	// To resize this to a 3*2 destination image, the intermediate is 12*6. +	// Whitespace has been added to delineate the destination pixels: +	//	aaab bbcc cddd +	//	aaab bbcc cddd +	//	eeef ffgg ghhh +	// +	//	eeef ffgg ghhh +	//	iiij jjkk klll +	//	iiij jjkk klll +	// Thus, the 'b' source pixel contributes one third of its value to the +	// (0, 0) destination pixel and two thirds to (1, 0). +	// The implementation is a two-step process. First, the source pixels are +	// iterated over and each source pixel's contribution to 1 or more +	// destination pixels are summed. Second, the sums are divided by a scaling +	// factor to yield the destination pixels. +	// TODO: By interleaving the two steps, instead of doing all of +	// step 1 first and all of step 2 second, we could allocate a smaller sum +	// slice of length 4*w*2 instead of 4*w*h, although the resultant code +	// would become more complicated. +	n, sum := dx*dy, make([]uint64, 4*w*h) +	for y := r.Min.Y; y < r.Max.Y; y++ { +		for x := r.Min.X; x < r.Max.X; x++ { +			// Get the source pixel. +			r32, g32, b32, a32 := m.At(x, y).RGBA() +			r64 := uint64(r32) +			g64 := uint64(g32) +			b64 := uint64(b32) +			a64 := uint64(a32) +			// Spread the source pixel over 1 or more destination rows. +			py := uint64(y) * hh +			for remy := hh; remy > 0; { +				qy := dy - (py % dy) +				if qy > remy { +					qy = remy +				} +				// Spread the source pixel over 1 or more destination columns. +				px := uint64(x) * ww +				index := 4 * ((py/dy)*ww + (px / dx)) +				for remx := ww; remx > 0; { +					qx := dx - (px % dx) +					if qx > remx { +						qx = remx +					} +					sum[index+0] += r64 * qx * qy +					sum[index+1] += g64 * qx * qy +					sum[index+2] += b64 * qx * qy +					sum[index+3] += a64 * qx * qy +					index += 4 +					px += qx +					remx -= qx +				} +				py += qy +				remy -= qy +			} +		} +	} +	return average(sum, w, h, n*0x0101) +} + +// average convert the sums to averages and returns the result. +func average(sum []uint64, w, h int, n uint64) image.Image { +	ret := image.NewRGBA(image.Rect(0, 0, w, h)) +	for y := 0; y < h; y++ { +		for x := 0; x < w; x++ { +			index := 4 * (y*w + x) +			ret.SetRGBA(x, y, color.RGBA{ +				uint8(sum[index+0] / n), +				uint8(sum[index+1] / n), +				uint8(sum[index+2] / n), +				uint8(sum[index+3] / n), +			}) +		} +	} +	return ret +} + +// resizeYCbCr returns a scaled copy of the YCbCr image slice r of m. +// The returned image has width w and height h. +func resizeYCbCr(m *image.YCbCr, r image.Rectangle, w, h int) (image.Image, bool) { +	var verticalRes int +	switch m.SubsampleRatio { +	case image.YCbCrSubsampleRatio420: +		verticalRes = 2 +	case image.YCbCrSubsampleRatio422: +		verticalRes = 1 +	default: +		return nil, false +	} +	ww, hh := uint64(w), uint64(h) +	dx, dy := uint64(r.Dx()), uint64(r.Dy()) +	// See comment in Resize. +	n, sum := dx*dy, make([]uint64, 4*w*h) +	for y := r.Min.Y; y < r.Max.Y; y++ { +		Y := m.Y[y*m.YStride:] +		Cb := m.Cb[y/verticalRes*m.CStride:] +		Cr := m.Cr[y/verticalRes*m.CStride:] +		for x := r.Min.X; x < r.Max.X; x++ { +			// Get the source pixel. +			r8, g8, b8 := color.YCbCrToRGB(Y[x], Cb[x/2], Cr[x/2]) +			r64 := uint64(r8) +			g64 := uint64(g8) +			b64 := uint64(b8) +			// Spread the source pixel over 1 or more destination rows. +			py := uint64(y) * hh +			for remy := hh; remy > 0; { +				qy := dy - (py % dy) +				if qy > remy { +					qy = remy +				} +				// Spread the source pixel over 1 or more destination columns. +				px := uint64(x) * ww +				index := 4 * ((py/dy)*ww + (px / dx)) +				for remx := ww; remx > 0; { +					qx := dx - (px % dx) +					if qx > remx { +						qx = remx +					} +					qxy := qx * qy +					sum[index+0] += r64 * qxy +					sum[index+1] += g64 * qxy +					sum[index+2] += b64 * qxy +					sum[index+3] += 0xFFFF * qxy +					index += 4 +					px += qx +					remx -= qx +				} +				py += qy +				remy -= qy +			} +		} +	} +	return average(sum, w, h, n), true +} + +// resizeRGBA returns a scaled copy of the RGBA image slice r of m. +// The returned image has width w and height h. +func resizeRGBA(m *image.RGBA, r image.Rectangle, w, h int) image.Image { +	ww, hh := uint64(w), uint64(h) +	dx, dy := uint64(r.Dx()), uint64(r.Dy()) +	// See comment in Resize. +	n, sum := dx*dy, make([]uint64, 4*w*h) +	for y := r.Min.Y; y < r.Max.Y; y++ { +		pixOffset := m.PixOffset(r.Min.X, y) +		for x := r.Min.X; x < r.Max.X; x++ { +			// Get the source pixel. +			r64 := uint64(m.Pix[pixOffset+0]) +			g64 := uint64(m.Pix[pixOffset+1]) +			b64 := uint64(m.Pix[pixOffset+2]) +			a64 := uint64(m.Pix[pixOffset+3]) +			pixOffset += 4 +			// Spread the source pixel over 1 or more destination rows. +			py := uint64(y) * hh +			for remy := hh; remy > 0; { +				qy := dy - (py % dy) +				if qy > remy { +					qy = remy +				} +				// Spread the source pixel over 1 or more destination columns. +				px := uint64(x) * ww +				index := 4 * ((py/dy)*ww + (px / dx)) +				for remx := ww; remx > 0; { +					qx := dx - (px % dx) +					if qx > remx { +						qx = remx +					} +					qxy := qx * qy +					sum[index+0] += r64 * qxy +					sum[index+1] += g64 * qxy +					sum[index+2] += b64 * qxy +					sum[index+3] += a64 * qxy +					index += 4 +					px += qx +					remx -= qx +				} +				py += qy +				remy -= qy +			} +		} +	} +	return average(sum, w, h, n) +} + +// Resample returns a resampled copy of the image slice r of m. +// The returned image has width w and height h. +func Resample(m image.Image, r image.Rectangle, w, h int) image.Image { +	if w < 0 || h < 0 { +		return nil +	} +	if w == 0 || h == 0 || r.Dx() <= 0 || r.Dy() <= 0 { +		return image.NewRGBA64(image.Rect(0, 0, w, h)) +	} +	curw, curh := r.Dx(), r.Dy() +	img := image.NewRGBA(image.Rect(0, 0, w, h)) +	for y := 0; y < h; y++ { +		for x := 0; x < w; x++ { +			// Get a source pixel. +			subx := x * curw / w +			suby := y * curh / h +			r32, g32, b32, a32 := m.At(subx, suby).RGBA() +			r := uint8(r32 >> 8) +			g := uint8(g32 >> 8) +			b := uint8(b32 >> 8) +			a := uint8(a32 >> 8) +			img.SetRGBA(x, y, color.RGBA{r, g, b, a}) +		} +	} +	return img +} diff --git a/mod/lightpics/vendors/jquery-file-upload/server/gae-go/static/favicon.ico b/mod/lightpics/vendors/jquery-file-upload/server/gae-go/static/favicon.icoBinary files differ new file mode 100644 index 000000000..1a71ea772 --- /dev/null +++ b/mod/lightpics/vendors/jquery-file-upload/server/gae-go/static/favicon.ico diff --git a/mod/lightpics/vendors/jquery-file-upload/server/gae-go/static/robots.txt b/mod/lightpics/vendors/jquery-file-upload/server/gae-go/static/robots.txt new file mode 100644 index 000000000..eb0536286 --- /dev/null +++ b/mod/lightpics/vendors/jquery-file-upload/server/gae-go/static/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/mod/lightpics/vendors/jquery-file-upload/server/gae-python/app.yaml b/mod/lightpics/vendors/jquery-file-upload/server/gae-python/app.yaml new file mode 100644 index 000000000..5fe123f59 --- /dev/null +++ b/mod/lightpics/vendors/jquery-file-upload/server/gae-python/app.yaml @@ -0,0 +1,16 @@ +application: jquery-file-upload +version: 1 +runtime: python27 +api_version: 1 +threadsafe: true + +builtins: +- deferred: on + +handlers: +- url: /(favicon\.ico|robots\.txt) +  static_files: static/\1 +  upload: static/(.*) +  expiration: '1d' +- url: /.* +  script: main.app diff --git a/mod/lightpics/vendors/jquery-file-upload/server/gae-python/main.py b/mod/lightpics/vendors/jquery-file-upload/server/gae-python/main.py new file mode 100644 index 000000000..37218c827 --- /dev/null +++ b/mod/lightpics/vendors/jquery-file-upload/server/gae-python/main.py @@ -0,0 +1,149 @@ +# -*- coding: utf-8 -*- +# +# jQuery File Upload Plugin GAE Python Example 1.1.4 +# https://github.com/blueimp/jQuery-File-Upload +# +# Copyright 2011, Sebastian Tschan +# https://blueimp.net +# +# Licensed under the MIT license: +# http://www.opensource.org/licenses/MIT +# + +from __future__ import with_statement +from google.appengine.api import files, images +from google.appengine.ext import blobstore, deferred +from google.appengine.ext.webapp import blobstore_handlers +import json, re, urllib, webapp2 + +WEBSITE = 'http://blueimp.github.com/jQuery-File-Upload/' +MIN_FILE_SIZE = 1 # bytes +MAX_FILE_SIZE = 5000000 # bytes +IMAGE_TYPES = re.compile('image/(gif|p?jpeg|(x-)?png)') +ACCEPT_FILE_TYPES = IMAGE_TYPES +THUMBNAIL_MODIFICATOR = '=s80' # max width / height +EXPIRATION_TIME = 300 # seconds + +def cleanup(blob_keys): +    blobstore.delete(blob_keys) + +class UploadHandler(webapp2.RequestHandler): + +    def initialize(self, request, response): +        super(UploadHandler, self).initialize(request, response) +        self.response.headers['Access-Control-Allow-Origin'] = '*' +        self.response.headers[ +            'Access-Control-Allow-Methods' +        ] = 'OPTIONS, HEAD, GET, POST, PUT, DELETE' +     +    def validate(self, file): +        if file['size'] < MIN_FILE_SIZE: +            file['error'] = 'minFileSize' +        elif file['size'] > MAX_FILE_SIZE: +            file['error'] = 'maxFileSize' +        elif not ACCEPT_FILE_TYPES.match(file['type']): +            file['error'] = 'acceptFileTypes' +        else: +            return True +        return False +     +    def get_file_size(self, file): +        file.seek(0, 2) # Seek to the end of the file +        size = file.tell() # Get the position of EOF +        file.seek(0) # Reset the file position to the beginning +        return size +     +    def write_blob(self, data, info): +        blob = files.blobstore.create( +            mime_type=info['type'], +            _blobinfo_uploaded_filename=info['name'] +        ) +        with files.open(blob, 'a') as f: +            f.write(data) +        files.finalize(blob) +        return files.blobstore.get_blob_key(blob) +     +    def handle_upload(self): +        results = [] +        blob_keys = [] +        for name, fieldStorage in self.request.POST.items(): +            if type(fieldStorage) is unicode: +                continue +            result = {} +            result['name'] = re.sub(r'^.*\\', '', +                fieldStorage.filename) +            result['type'] = fieldStorage.type +            result['size'] = self.get_file_size(fieldStorage.file) +            if self.validate(result): +                blob_key = str( +                    self.write_blob(fieldStorage.value, result) +                ) +                blob_keys.append(blob_key) +                result['delete_type'] = 'DELETE' +                result['delete_url'] = self.request.host_url +\ +                    '/?key=' + urllib.quote(blob_key, '') +                if (IMAGE_TYPES.match(result['type'])): +                    try: +                        result['url'] = images.get_serving_url( +                            blob_key, +                            secure_url=self.request.host_url\ +                                .startswith('https') +                        ) +                        result['thumbnail_url'] = result['url'] +\ +                            THUMBNAIL_MODIFICATOR +                    except: # Could not get an image serving url +                        pass +                if not 'url' in result: +                    result['url'] = self.request.host_url +\ +                        '/' + blob_key + '/' + urllib.quote( +                            result['name'].encode('utf-8'), '') +            results.append(result) +        deferred.defer( +            cleanup, +            blob_keys, +            _countdown=EXPIRATION_TIME +        ) +        return results +     +    def options(self): +        pass +         +    def head(self): +        pass +     +    def get(self): +        self.redirect(WEBSITE) +     +    def post(self): +        if (self.request.get('_method') == 'DELETE'): +            return self.delete() +        s = json.dumps(self.handle_upload(), separators=(',',':')) +        redirect = self.request.get('redirect') +        if redirect: +            return self.redirect(str( +                redirect.replace('%s', urllib.quote(s, ''), 1) +            )) +        if 'application/json' in self.request.headers.get('Accept'): +            self.response.headers['Content-Type'] = 'application/json' +        self.response.write(s) + +    def delete(self): +        blobstore.delete(self.request.get('key') or '') + +class DownloadHandler(blobstore_handlers.BlobstoreDownloadHandler): +    def get(self, key, filename): +        if not blobstore.get(key): +            self.error(404) +        else: +            # Cache for the expiration time: +            self.response.headers['Cache-Control'] =\ +                'public,max-age=%d' % EXPIRATION_TIME +            self.send_blob(key, save_as=filename) + +app = webapp2.WSGIApplication( +    [ +        ('/', UploadHandler), +        ('/([^/]+)/([^/]+)', DownloadHandler) +    ], +    debug=True +)
\ No newline at end of file diff --git a/mod/lightpics/vendors/jquery-file-upload/server/gae-python/static/favicon.ico b/mod/lightpics/vendors/jquery-file-upload/server/gae-python/static/favicon.icoBinary files differ new file mode 100644 index 000000000..1a71ea772 --- /dev/null +++ b/mod/lightpics/vendors/jquery-file-upload/server/gae-python/static/favicon.ico diff --git a/mod/lightpics/vendors/jquery-file-upload/server/gae-python/static/robots.txt b/mod/lightpics/vendors/jquery-file-upload/server/gae-python/static/robots.txt new file mode 100644 index 000000000..eb0536286 --- /dev/null +++ b/mod/lightpics/vendors/jquery-file-upload/server/gae-python/static/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/mod/lightpics/vendors/jquery-file-upload/server/node/.gitignore b/mod/lightpics/vendors/jquery-file-upload/server/node/.gitignore new file mode 100644 index 000000000..9daa8247d --- /dev/null +++ b/mod/lightpics/vendors/jquery-file-upload/server/node/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +node_modules diff --git a/mod/lightpics/vendors/jquery-file-upload/server/node/package.json b/mod/lightpics/vendors/jquery-file-upload/server/node/package.json new file mode 100644 index 000000000..0e0c1aaae --- /dev/null +++ b/mod/lightpics/vendors/jquery-file-upload/server/node/package.json @@ -0,0 +1,41 @@ +{ +    "name": "blueimp-file-upload-node", +    "version": "1.0.2", +    "title": "jQuery File Upload Node.js example", +    "description": "Node.js implementation example of a file upload handler for jQuery File Upload.", +    "keywords": [ +        "file", +        "upload", +        "cross-domain", +        "cross-site", +        "node" +    ], +    "homepage": "https://github.com/blueimp/jQuery-File-Upload", +    "author": { +        "name": "Sebastian Tschan", +        "url": "https://blueimp.net" +    }, +    "maintainers": [ +        { +            "name": "Sebastian Tschan", +            "url": "https://blueimp.net" +        } +    ], +    "repository": { +        "type": "git", +        "url": "git://github.com/blueimp/jQuery-File-Upload.git" +    }, +    "bugs": "https://github.com/blueimp/jQuery-File-Upload/issues", +    "licenses": [ +        { +            "type": "MIT", +            "url": "http://www.opensource.org/licenses/MIT" +        } +    ], +    "dependencies": { +        "formidable": ">=1.0.8", +        "node-static": ">=0.5.9", +        "imagemagick": ">=0.1.2" +    }, +    "main": "server.js" +} diff --git a/mod/lightpics/vendors/jquery-file-upload/server/node/public/files/thumbnail/.gitignore b/mod/lightpics/vendors/jquery-file-upload/server/node/public/files/thumbnail/.gitignore new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/mod/lightpics/vendors/jquery-file-upload/server/node/public/files/thumbnail/.gitignore diff --git a/mod/lightpics/vendors/jquery-file-upload/server/node/server.js b/mod/lightpics/vendors/jquery-file-upload/server/node/server.js new file mode 100755 index 000000000..f1bec542b --- /dev/null +++ b/mod/lightpics/vendors/jquery-file-upload/server/node/server.js @@ -0,0 +1,285 @@ +#!/usr/bin/env node +/* + * jQuery File Upload Plugin Node.js Example 1.0.2 + * https://github.com/blueimp/jQuery-File-Upload + * + * Copyright 2012, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +/*jslint nomen: true, regexp: true, unparam: true */ +/*global require, __dirname, unescape */ + +(function (port) { +    'use strict'; +    var path = require('path'), +        fs = require('fs'), +        // Since Node 0.8, .existsSync() moved from path to fs: +        _existsSync = fs.existsSync || path.existsSync, +        formidable = require('formidable'), +        nodeStatic = require('node-static'), +        imageMagick = require('imagemagick'), +        options = { +            tmpDir: __dirname + '/tmp', +            publicDir: __dirname + '/public', +            uploadDir: __dirname + '/public/files', +            uploadUrl: '/files/', +            maxPostSize: 500000000, // 500 MB +            minFileSize: 1, +            maxFileSize: 100000000, // 100 MB +            acceptFileTypes: /.+/i, +            // Files not matched by this regular expression force a download dialog, +            // to prevent executing any scripts in the context of the service domain: +            safeFileTypes: /\.(gif|jpe?g|png)$/i, +            imageTypes: /\.(gif|jpe?g|png)$/i, +            imageVersions: { +                'thumbnail': { +                    width: 80, +                    height: 80 +                } +            }, +            accessControl: { +                allowOrigin: '*', +                allowMethods: 'OPTIONS, HEAD, GET, POST, PUT, DELETE' +            }, +            /* Uncomment and edit this section to provide the service via HTTPS: +            ssl: { +                key: fs.readFileSync('/Applications/XAMPP/etc/ssl.key/server.key'), +                cert: fs.readFileSync('/Applications/XAMPP/etc/ssl.crt/server.crt') +            }, +            */ +            nodeStatic: { +                cache: 3600 // seconds to cache served files +            } +        }, +        utf8encode = function (str) { +            return unescape(encodeURIComponent(str)); +        }, +        fileServer = new nodeStatic.Server(options.publicDir, options.nodeStatic), +        nameCountRegexp = /(?:(?: \(([\d]+)\))?(\.[^.]+))?$/, +        nameCountFunc = function (s, index, ext) { +            return ' (' + ((parseInt(index, 10) || 0) + 1) + ')' + (ext || ''); +        }, +        FileInfo = function (file) { +            this.name = file.name; +            this.size = file.size; +            this.type = file.type; +            this.delete_type = 'DELETE'; +        }, +        UploadHandler = function (req, res, callback) { +            this.req = req; +            this.res = res; +            this.callback = callback; +        }, +        serve = function (req, res) { +            res.setHeader( +                'Access-Control-Allow-Origin', +                options.accessControl.allowOrigin +            ); +            res.setHeader( +                'Access-Control-Allow-Methods', +                options.accessControl.allowMethods +            ); +            var handleResult = function (result, redirect) { +                    if (redirect) { +                        res.writeHead(302, { +                            'Location': redirect.replace( +                                /%s/, +                                encodeURIComponent(JSON.stringify(result)) +                            ) +                        }); +                        res.end(); +                    } else { +                        res.writeHead(200, { +                            'Content-Type': req.headers.accept +                                .indexOf('application/json') !== -1 ? +                                        'application/json' : 'text/plain' +                        }); +                        res.end(JSON.stringify(result)); +                    } +                }, +                setNoCacheHeaders = function () { +                    res.setHeader('Pragma', 'no-cache'); +                    res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate'); +                    res.setHeader('Content-Disposition', 'inline; filename="files.json"'); +                }, +                handler = new UploadHandler(req, res, handleResult); +            switch (req.method) { +            case 'OPTIONS': +                res.end(); +                break; +            case 'HEAD': +            case 'GET': +                if (req.url === '/') { +                    setNoCacheHeaders(); +                    if (req.method === 'GET') { +                        handler.get(); +                    } else { +                        res.end(); +                    } +                } else { +                    fileServer.serve(req, res); +                } +                break; +            case 'POST': +                setNoCacheHeaders(); +                handler.post(); +                break; +            case 'DELETE': +                handler.destroy(); +                break; +            default: +                res.statusCode = 405; +                res.end(); +            } +        }; +    fileServer.respond = function (pathname, status, _headers, files, stat, req, res, finish) { +        if (!options.safeFileTypes.test(files[0])) { +            // Force a download dialog for unsafe file extensions: +            res.setHeader( +                'Content-Disposition', +                'attachment; filename="' + utf8encode(path.basename(files[0])) + '"' +            ); +        } else { +            // Prevent Internet Explorer from MIME-sniffing the content-type: +            res.setHeader('X-Content-Type-Options', 'nosniff'); +        } +        nodeStatic.Server.prototype.respond +            .call(this, pathname, status, _headers, files, stat, req, res, finish); +    }; +    FileInfo.prototype.validate = function () { +        if (options.minFileSize && options.minFileSize > this.size) { +            this.error = 'minFileSize'; +        } else if (options.maxFileSize && options.maxFileSize < this.size) { +            this.error = 'maxFileSize'; +        } else if (!options.acceptFileTypes.test(this.name)) { +            this.error = 'acceptFileTypes'; +        } +        return !this.error; +    }; +    FileInfo.prototype.safeName = function () { +        // Prevent directory traversal and creating hidden system files: +        this.name = path.basename(this.name).replace(/^\.+/, ''); +        // Prevent overwriting existing files: +        while (_existsSync(options.uploadDir + '/' + this.name)) { +            this.name = this.name.replace(nameCountRegexp, nameCountFunc); +        } +    }; +    FileInfo.prototype.initUrls = function (req) { +        if (!this.error) { +            var that = this, +                baseUrl = (options.ssl ? 'https:' : 'http:') + +                    '//' + req.headers.host + options.uploadUrl; +            this.url = this.delete_url = baseUrl + encodeURIComponent(this.name); +            Object.keys(options.imageVersions).forEach(function (version) { +                if (_existsSync( +                        options.uploadDir + '/' + version + '/' + that.name +                    )) { +                    that[version + '_url'] = baseUrl + version + '/' + +                        encodeURIComponent(that.name); +                } +            }); +        } +    }; +    UploadHandler.prototype.get = function () { +        var handler = this, +            files = []; +        fs.readdir(options.uploadDir, function (err, list) { +            list.forEach(function (name) { +                var stats = fs.statSync(options.uploadDir + '/' + name), +                    fileInfo; +                if (stats.isFile()) { +                    fileInfo = new FileInfo({ +                        name: name, +                        size: stats.size +                    }); +                    fileInfo.initUrls(handler.req); +                    files.push(fileInfo); +                } +            }); +            handler.callback(files); +        }); +    }; +    UploadHandler.prototype.post = function () { +        var handler = this, +            form = new formidable.IncomingForm(), +            tmpFiles = [], +            files = [], +            map = {}, +            counter = 1, +            redirect, +            finish = function () { +                counter -= 1; +                if (!counter) { +                    files.forEach(function (fileInfo) { +                        fileInfo.initUrls(handler.req); +                    }); +                    handler.callback(files, redirect); +                } +            }; +        form.uploadDir = options.tmpDir; +        form.on('fileBegin', function (name, file) { +            tmpFiles.push(file.path); +            var fileInfo = new FileInfo(file, handler.req, true); +            fileInfo.safeName(); +            map[path.basename(file.path)] = fileInfo; +            files.push(fileInfo); +        }).on('field', function (name, value) { +            if (name === 'redirect') { +                redirect = value; +            } +        }).on('file', function (name, file) { +            var fileInfo = map[path.basename(file.path)]; +            fileInfo.size = file.size; +            if (!fileInfo.validate()) { +                fs.unlink(file.path); +                return; +            } +            fs.renameSync(file.path, options.uploadDir + '/' + fileInfo.name); +            if (options.imageTypes.test(fileInfo.name)) { +                Object.keys(options.imageVersions).forEach(function (version) { +                    counter += 1; +                    var opts = options.imageVersions[version]; +                    imageMagick.resize({ +                        width: opts.width, +                        height: opts.height, +                        srcPath: options.uploadDir + '/' + fileInfo.name, +                        dstPath: options.uploadDir + '/' + version + '/' + +                            fileInfo.name +                    }, finish); +                }); +            } +        }).on('aborted', function () { +            tmpFiles.forEach(function (file) { +                fs.unlink(file); +            }); +        }).on('progress', function (bytesReceived, bytesExpected) { +            if (bytesReceived > options.maxPostSize) { +                handler.req.connection.destroy(); +            } +        }).on('end', finish).parse(handler.req); +    }; +    UploadHandler.prototype.destroy = function () { +        var handler = this, +            fileName; +        if (handler.req.url.slice(0, options.uploadUrl.length) === options.uploadUrl) { +            fileName = path.basename(decodeURIComponent(handler.req.url)); +            fs.unlink(options.uploadDir + '/' + fileName, function (ex) { +                Object.keys(options.imageVersions).forEach(function (version) { +                    fs.unlink(options.uploadDir + '/' + version + '/' + fileName); +                }); +                handler.callback(!ex); +            }); +        } else { +            handler.callback(false); +        } +    }; +    if (options.ssl) { +        require('https').createServer(options.ssl, serve).listen(port); +    } else { +        require('http').createServer(serve).listen(port); +    } +}(8888)); diff --git a/mod/lightpics/vendors/jquery-file-upload/server/node/tmp/.gitignore b/mod/lightpics/vendors/jquery-file-upload/server/node/tmp/.gitignore new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/mod/lightpics/vendors/jquery-file-upload/server/node/tmp/.gitignore diff --git a/mod/lightpics/vendors/jquery-file-upload/server/php/files/.htaccess b/mod/lightpics/vendors/jquery-file-upload/server/php/files/.htaccess new file mode 100644 index 000000000..a6a9f6a75 --- /dev/null +++ b/mod/lightpics/vendors/jquery-file-upload/server/php/files/.htaccess @@ -0,0 +1,4 @@ +ForceType application/octet-stream +<FilesMatch "(?i)\.(gif|jpe?g|png)$"> +  ForceType none +</FilesMatch>
\ No newline at end of file diff --git a/mod/lightpics/vendors/jquery-file-upload/server/php/index.php b/mod/lightpics/vendors/jquery-file-upload/server/php/index.php new file mode 100644 index 000000000..1601c76f3 --- /dev/null +++ b/mod/lightpics/vendors/jquery-file-upload/server/php/index.php @@ -0,0 +1,46 @@ +<?php +/* + * jQuery File Upload Plugin PHP Example 5.7 + * https://github.com/blueimp/jQuery-File-Upload + * + * Copyright 2010, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +error_reporting(E_ALL | E_STRICT); + +require('upload.class.php'); + +$upload_handler = new UploadHandler(); + +header('Pragma: no-cache'); +header('Cache-Control: no-store, no-cache, must-revalidate'); +header('Content-Disposition: inline; filename="files.json"'); +header('X-Content-Type-Options: nosniff'); +header('Access-Control-Allow-Origin: *'); +header('Access-Control-Allow-Methods: OPTIONS, HEAD, GET, POST, PUT, DELETE'); +header('Access-Control-Allow-Headers: X-File-Name, X-File-Type, X-File-Size'); + +switch ($_SERVER['REQUEST_METHOD']) { +    case 'OPTIONS': +        break; +    case 'HEAD': +    case 'GET': +        $upload_handler->get(); +        break; +    case 'POST': +        if (isset($_REQUEST['_method']) && $_REQUEST['_method'] === 'DELETE') { +            $upload_handler->delete(); +        } else { +            $upload_handler->post(); +        } +        break; +    case 'DELETE': +        $upload_handler->delete(); +        break; +    default: +        header('HTTP/1.1 405 Method Not Allowed'); +} diff --git a/mod/lightpics/vendors/jquery-file-upload/server/php/thumbnails/.htaccess b/mod/lightpics/vendors/jquery-file-upload/server/php/thumbnails/.htaccess new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/mod/lightpics/vendors/jquery-file-upload/server/php/thumbnails/.htaccess diff --git a/mod/lightpics/vendors/jquery-file-upload/server/php/upload.class.php b/mod/lightpics/vendors/jquery-file-upload/server/php/upload.class.php new file mode 100644 index 000000000..c4efacbdb --- /dev/null +++ b/mod/lightpics/vendors/jquery-file-upload/server/php/upload.class.php @@ -0,0 +1,436 @@ +<?php +/* + * jQuery File Upload Plugin PHP Class 5.11.2 + * https://github.com/blueimp/jQuery-File-Upload + * + * Copyright 2010, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +class UploadHandler +{ +    protected $options; + +    function __construct($options=null) { +        $this->options = array( +            'script_url' => $this->getFullUrl().'/', +            'upload_dir' => dirname($_SERVER['SCRIPT_FILENAME']).'/files/', +            'upload_url' => $this->getFullUrl().'/files/', +            'param_name' => 'files', +            // Set the following option to 'POST', if your server does not support +            // DELETE requests. This is a parameter sent to the client: +            'delete_type' => 'DELETE', +            // The php.ini settings upload_max_filesize and post_max_size +            // take precedence over the following max_file_size setting: +            'max_file_size' => null, +            'min_file_size' => 1, +            'accept_file_types' => '/.+$/i', +            // The maximum number of files for the upload directory: +            'max_number_of_files' => null, +            // Image resolution restrictions: +            'max_width' => null, +            'max_height' => null, +            'min_width' => 1, +            'min_height' => 1, +            // Set the following option to false to enable resumable uploads: +            'discard_aborted_uploads' => true, +            // Set to true to rotate images based on EXIF meta data, if available: +            'orient_image' => false, +            'image_versions' => array( +                // Uncomment the following version to restrict the size of +                // uploaded images. You can also add additional versions with +                // their own upload directories: +                /* +                'large' => array( +                    'upload_dir' => dirname($_SERVER['SCRIPT_FILENAME']).'/files/', +                    'upload_url' => $this->getFullUrl().'/files/', +                    'max_width' => 1920, +                    'max_height' => 1200, +                    'jpeg_quality' => 95 +                ), +                */ +                'thumbnail' => array( +                    'upload_dir' => dirname($_SERVER['SCRIPT_FILENAME']).'/thumbnails/', +                    'upload_url' => $this->getFullUrl().'/thumbnails/', +                    'max_width' => 80, +                    'max_height' => 80 +                ) +            ) +        ); +        if ($options) { +            $this->options = array_replace_recursive($this->options, $options); +        } +    } + +    protected function getFullUrl() { +        $https = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'; +      	return +    		($https ? 'https://' : 'http://'). +    		(!empty($_SERVER['REMOTE_USER']) ? $_SERVER['REMOTE_USER'].'@' : ''). +    		(isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ($_SERVER['SERVER_NAME']. +    		($https && $_SERVER['SERVER_PORT'] === 443 || +    		$_SERVER['SERVER_PORT'] === 80 ? '' : ':'.$_SERVER['SERVER_PORT']))). +    		substr($_SERVER['SCRIPT_NAME'],0, strrpos($_SERVER['SCRIPT_NAME'], '/')); +    } + +    protected function set_file_delete_url($file) { +        $file->delete_url = $this->options['script_url'] +            .'?file='.rawurlencode($file->name); +        $file->delete_type = $this->options['delete_type']; +        if ($file->delete_type !== 'DELETE') { +            $file->delete_url .= '&_method=DELETE'; +        } +    } + +    protected function get_file_object($file_name) { +        $file_path = $this->options['upload_dir'].$file_name; +        if (is_file($file_path) && $file_name[0] !== '.') { +            $file = new stdClass(); +            $file->name = $file_name; +            $file->size = filesize($file_path); +            $file->url = $this->options['upload_url'].rawurlencode($file->name); +            foreach($this->options['image_versions'] as $version => $options) { +                if (is_file($options['upload_dir'].$file_name)) { +                    $file->{$version.'_url'} = $options['upload_url'] +                        .rawurlencode($file->name); +                } +            } +            $this->set_file_delete_url($file); +            return $file; +        } +        return null; +    } + +    protected function get_file_objects() { +        return array_values(array_filter(array_map( +            array($this, 'get_file_object'), +            scandir($this->options['upload_dir']) +        ))); +    } + +    protected function create_scaled_image($file_name, $options) { +        $file_path = $this->options['upload_dir'].$file_name; +        $new_file_path = $options['upload_dir'].$file_name; +        list($img_width, $img_height) = @getimagesize($file_path); +        if (!$img_width || !$img_height) { +            return false; +        } +        $scale = min( +            $options['max_width'] / $img_width, +            $options['max_height'] / $img_height +        ); +        if ($scale >= 1) { +            if ($file_path !== $new_file_path) { +                return copy($file_path, $new_file_path); +            } +            return true; +        } +        $new_width = $img_width * $scale; +        $new_height = $img_height * $scale; +        $new_img = @imagecreatetruecolor($new_width, $new_height); +        switch (strtolower(substr(strrchr($file_name, '.'), 1))) { +            case 'jpg': +            case 'jpeg': +                $src_img = @imagecreatefromjpeg($file_path); +                $write_image = 'imagejpeg'; +                $image_quality = isset($options['jpeg_quality']) ? +                    $options['jpeg_quality'] : 75; +                break; +            case 'gif': +                @imagecolortransparent($new_img, @imagecolorallocate($new_img, 0, 0, 0)); +                $src_img = @imagecreatefromgif($file_path); +                $write_image = 'imagegif'; +                $image_quality = null; +                break; +            case 'png': +                @imagecolortransparent($new_img, @imagecolorallocate($new_img, 0, 0, 0)); +                @imagealphablending($new_img, false); +                @imagesavealpha($new_img, true); +                $src_img = @imagecreatefrompng($file_path); +                $write_image = 'imagepng'; +                $image_quality = isset($options['png_quality']) ? +                    $options['png_quality'] : 9; +                break; +            default: +                $src_img = null; +        } +        $success = $src_img && @imagecopyresampled( +            $new_img, +            $src_img, +            0, 0, 0, 0, +            $new_width, +            $new_height, +            $img_width, +            $img_height +        ) && $write_image($new_img, $new_file_path, $image_quality); +        // Free up memory (imagedestroy does not delete files): +        @imagedestroy($src_img); +        @imagedestroy($new_img); +        return $success; +    } + +    protected function validate($uploaded_file, $file, $error, $index) { +        if ($error) { +            $file->error = $error; +            return false; +        } +        if (!$file->name) { +            $file->error = 'missingFileName'; +            return false; +        } +        if (!preg_match($this->options['accept_file_types'], $file->name)) { +            $file->error = 'acceptFileTypes'; +            return false; +        } +        if ($uploaded_file && is_uploaded_file($uploaded_file)) { +            $file_size = filesize($uploaded_file); +        } else { +            $file_size = $_SERVER['CONTENT_LENGTH']; +        } +        if ($this->options['max_file_size'] && ( +                $file_size > $this->options['max_file_size'] || +                $file->size > $this->options['max_file_size']) +            ) { +            $file->error = 'maxFileSize'; +            return false; +        } +        if ($this->options['min_file_size'] && +            $file_size < $this->options['min_file_size']) { +            $file->error = 'minFileSize'; +            return false; +        } +        if (is_int($this->options['max_number_of_files']) && ( +                count($this->get_file_objects()) >= $this->options['max_number_of_files']) +            ) { +            $file->error = 'maxNumberOfFiles'; +            return false; +        } +        list($img_width, $img_height) = @getimagesize($uploaded_file); +        if (is_int($img_width)) { +            if ($this->options['max_width'] && $img_width > $this->options['max_width'] || +                    $this->options['max_height'] && $img_height > $this->options['max_height']) { +                $file->error = 'maxResolution'; +                return false; +            } +            if ($this->options['min_width'] && $img_width < $this->options['min_width'] || +                    $this->options['min_height'] && $img_height < $this->options['min_height']) { +                $file->error = 'minResolution'; +                return false; +            } +        } +        return true; +    } + +    protected function upcount_name_callback($matches) { +        $index = isset($matches[1]) ? intval($matches[1]) + 1 : 1; +        $ext = isset($matches[2]) ? $matches[2] : ''; +        return ' ('.$index.')'.$ext; +    } + +    protected function upcount_name($name) { +        return preg_replace_callback( +            '/(?:(?: \(([\d]+)\))?(\.[^.]+))?$/', +            array($this, 'upcount_name_callback'), +            $name, +            1 +        ); +    } + +    protected function trim_file_name($name, $type, $index) { +        // Remove path information and dots around the filename, to prevent uploading +        // into different directories or replacing hidden system files. +        // Also remove control characters and spaces (\x00..\x20) around the filename: +        $file_name = trim(basename(stripslashes($name)), ".\x00..\x20"); +        // Add missing file extension for known image types: +        if (strpos($file_name, '.') === false && +            preg_match('/^image\/(gif|jpe?g|png)/', $type, $matches)) { +            $file_name .= '.'.$matches[1]; +        } +        if ($this->options['discard_aborted_uploads']) { +            while(is_file($this->options['upload_dir'].$file_name)) { +                $file_name = $this->upcount_name($file_name); +            } +        } +        return $file_name; +    } + +    protected function handle_form_data($file, $index) { +        // Handle form data, e.g. $_REQUEST['description'][$index] +    } + +    protected function orient_image($file_path) { +      	$exif = @exif_read_data($file_path); +        if ($exif === false) { +            return false; +        } +      	$orientation = intval(@$exif['Orientation']); +      	if (!in_array($orientation, array(3, 6, 8))) { +      	    return false; +      	} +      	$image = @imagecreatefromjpeg($file_path); +      	switch ($orientation) { +        	  case 3: +          	    $image = @imagerotate($image, 180, 0); +          	    break; +        	  case 6: +          	    $image = @imagerotate($image, 270, 0); +          	    break; +        	  case 8: +          	    $image = @imagerotate($image, 90, 0); +          	    break; +          	default: +          	    return false; +      	} +      	$success = imagejpeg($image, $file_path); +      	// Free up memory (imagedestroy does not delete files): +      	@imagedestroy($image); +      	return $success; +    } + +    protected function handle_file_upload($uploaded_file, $name, $size, $type, $error, $index = null) { +        $file = new stdClass(); +        $file->name = $this->trim_file_name($name, $type, $index); +        $file->size = intval($size); +        $file->type = $type; +        if ($this->validate($uploaded_file, $file, $error, $index)) { +            $this->handle_form_data($file, $index); +            $file_path = $this->options['upload_dir'].$file->name; +            $append_file = !$this->options['discard_aborted_uploads'] && +                is_file($file_path) && $file->size > filesize($file_path); +            clearstatcache(); +            if ($uploaded_file && is_uploaded_file($uploaded_file)) { +                // multipart/formdata uploads (POST method uploads) +                if ($append_file) { +                    file_put_contents( +                        $file_path, +                        fopen($uploaded_file, 'r'), +                        FILE_APPEND +                    ); +                } else { +                    move_uploaded_file($uploaded_file, $file_path); +                } +            } else { +                // Non-multipart uploads (PUT method support) +                file_put_contents( +                    $file_path, +                    fopen('php://input', 'r'), +                    $append_file ? FILE_APPEND : 0 +                ); +            } +            $file_size = filesize($file_path); +            if ($file_size === $file->size) { +            	if ($this->options['orient_image']) { +            		$this->orient_image($file_path); +            	} +                $file->url = $this->options['upload_url'].rawurlencode($file->name); +                foreach($this->options['image_versions'] as $version => $options) { +                    if ($this->create_scaled_image($file->name, $options)) { +                        if ($this->options['upload_dir'] !== $options['upload_dir']) { +                            $file->{$version.'_url'} = $options['upload_url'] +                                .rawurlencode($file->name); +                        } else { +                            clearstatcache(); +                            $file_size = filesize($file_path); +                        } +                    } +                } +            } else if ($this->options['discard_aborted_uploads']) { +                unlink($file_path); +                $file->error = 'abort'; +            } +            $file->size = $file_size; +            $this->set_file_delete_url($file); +        } +        return $file; +    } + +    public function get() { +        $file_name = isset($_REQUEST['file']) ? +            basename(stripslashes($_REQUEST['file'])) : null; +        if ($file_name) { +            $info = $this->get_file_object($file_name); +        } else { +            $info = $this->get_file_objects(); +        } +        header('Content-type: application/json'); +        echo json_encode($info); +    } + +    public function post() { +        if (isset($_REQUEST['_method']) && $_REQUEST['_method'] === 'DELETE') { +            return $this->delete(); +        } +        $upload = isset($_FILES[$this->options['param_name']]) ? +            $_FILES[$this->options['param_name']] : null; +        $info = array(); +        if ($upload && is_array($upload['tmp_name'])) { +            // param_name is an array identifier like "files[]", +            // $_FILES is a multi-dimensional array: +            foreach ($upload['tmp_name'] as $index => $value) { +                $info[] = $this->handle_file_upload( +                    $upload['tmp_name'][$index], +                    isset($_SERVER['HTTP_X_FILE_NAME']) ? +                        $_SERVER['HTTP_X_FILE_NAME'] : $upload['name'][$index], +                    isset($_SERVER['HTTP_X_FILE_SIZE']) ? +                        $_SERVER['HTTP_X_FILE_SIZE'] : $upload['size'][$index], +                    isset($_SERVER['HTTP_X_FILE_TYPE']) ? +                        $_SERVER['HTTP_X_FILE_TYPE'] : $upload['type'][$index], +                    $upload['error'][$index], +                    $index +                ); +            } +        } elseif ($upload || isset($_SERVER['HTTP_X_FILE_NAME'])) { +            // param_name is a single object identifier like "file", +            // $_FILES is a one-dimensional array: +            $info[] = $this->handle_file_upload( +                isset($upload['tmp_name']) ? $upload['tmp_name'] : null, +                isset($_SERVER['HTTP_X_FILE_NAME']) ? +                    $_SERVER['HTTP_X_FILE_NAME'] : (isset($upload['name']) ? +                        $upload['name'] : null), +                isset($_SERVER['HTTP_X_FILE_SIZE']) ? +                    $_SERVER['HTTP_X_FILE_SIZE'] : (isset($upload['size']) ? +                        $upload['size'] : null), +                isset($_SERVER['HTTP_X_FILE_TYPE']) ? +                    $_SERVER['HTTP_X_FILE_TYPE'] : (isset($upload['type']) ? +                        $upload['type'] : null), +                isset($upload['error']) ? $upload['error'] : null +            ); +        } +        header('Vary: Accept'); +        $json = json_encode($info); +        $redirect = isset($_REQUEST['redirect']) ? +            stripslashes($_REQUEST['redirect']) : null; +        if ($redirect) { +            header('Location: '.sprintf($redirect, rawurlencode($json))); +            return; +        } +        if (isset($_SERVER['HTTP_ACCEPT']) && +            (strpos($_SERVER['HTTP_ACCEPT'], 'application/json') !== false)) { +            header('Content-type: application/json'); +        } else { +            header('Content-type: text/plain'); +        } +        echo $json; +    } + +    public function delete() { +        $file_name = isset($_REQUEST['file']) ? +            basename(stripslashes($_REQUEST['file'])) : null; +        $file_path = $this->options['upload_dir'].$file_name; +        $success = is_file($file_path) && $file_name[0] !== '.' && unlink($file_path); +        if ($success) { +            foreach($this->options['image_versions'] as $version => $options) { +                $file = $options['upload_dir'].$file_name; +                if (is_file($file)) { +                    unlink($file); +                } +            } +        } +        header('Content-type: application/json'); +        echo json_encode($success); +    } + +} | 
