Only commit raw text after OCR for now.
[redakcja.git] / src / fileupload / static / lib / jQuery-File-Upload-10.32.0 / server / gae-python / main.py
1 # -*- coding: utf-8 -*-
2 #
3 # jQuery File Upload Plugin GAE Python Example
4 # https://github.com/blueimp/jQuery-File-Upload
5 #
6 # Copyright 2011, Sebastian Tschan
7 # https://blueimp.net
8 #
9 # Licensed under the MIT license:
10 # https://opensource.org/licenses/MIT
11 #
12
13 from google.appengine.api import memcache, images
14 import json
15 import os
16 import re
17 import urllib
18 import webapp2
19
20 DEBUG=os.environ.get('SERVER_SOFTWARE', '').startswith('Dev')
21 WEBSITE = 'https://blueimp.github.io/jQuery-File-Upload/'
22 MIN_FILE_SIZE = 1  # bytes
23 # Max file size is memcache limit (1MB) minus key size minus overhead:
24 MAX_FILE_SIZE = 999000  # bytes
25 IMAGE_TYPES = re.compile('image/(gif|p?jpeg|(x-)?png)')
26 ACCEPT_FILE_TYPES = IMAGE_TYPES
27 THUMB_MAX_WIDTH = 80
28 THUMB_MAX_HEIGHT = 80
29 THUMB_SUFFIX = '.'+str(THUMB_MAX_WIDTH)+'x'+str(THUMB_MAX_HEIGHT)+'.png'
30 EXPIRATION_TIME = 300  # seconds
31 # If set to None, only allow redirects to the referer protocol+host.
32 # Set to a regexp for custom pattern matching against the redirect value:
33 REDIRECT_ALLOW_TARGET = None
34
35 class CORSHandler(webapp2.RequestHandler):
36     def cors(self):
37         headers = self.response.headers
38         headers['Access-Control-Allow-Origin'] = '*'
39         headers['Access-Control-Allow-Methods'] =\
40             'OPTIONS, HEAD, GET, POST, DELETE'
41         headers['Access-Control-Allow-Headers'] =\
42             'Content-Type, Content-Range, Content-Disposition'
43
44     def initialize(self, request, response):
45         super(CORSHandler, self).initialize(request, response)
46         self.cors()
47
48     def json_stringify(self, obj):
49         return json.dumps(obj, separators=(',', ':'))
50
51     def options(self, *args, **kwargs):
52         pass
53
54 class UploadHandler(CORSHandler):
55     def validate(self, file):
56         if file['size'] < MIN_FILE_SIZE:
57             file['error'] = 'File is too small'
58         elif file['size'] > MAX_FILE_SIZE:
59             file['error'] = 'File is too big'
60         elif not ACCEPT_FILE_TYPES.match(file['type']):
61             file['error'] = 'Filetype not allowed'
62         else:
63             return True
64         return False
65
66     def validate_redirect(self, redirect):
67         if redirect:
68             if REDIRECT_ALLOW_TARGET:
69                 return REDIRECT_ALLOW_TARGET.match(redirect)
70             referer = self.request.headers['referer']
71             if referer:
72                 from urlparse import urlparse
73                 parts = urlparse(referer)
74                 redirect_allow_target = '^' + re.escape(
75                     parts.scheme + '://' + parts.netloc + '/'
76                 )
77             return re.match(redirect_allow_target, redirect)
78         return False
79
80     def get_file_size(self, file):
81         file.seek(0, 2)  # Seek to the end of the file
82         size = file.tell()  # Get the position of EOF
83         file.seek(0)  # Reset the file position to the beginning
84         return size
85
86     def write_blob(self, data, info):
87         key = urllib.quote(info['type'].encode('utf-8'), '') +\
88             '/' + str(hash(data)) +\
89             '/' + urllib.quote(info['name'].encode('utf-8'), '')
90         try:
91             memcache.set(key, data, time=EXPIRATION_TIME)
92         except: #Failed to add to memcache
93             return (None, None)
94         thumbnail_key = None
95         if IMAGE_TYPES.match(info['type']):
96             try:
97                 img = images.Image(image_data=data)
98                 img.resize(
99                     width=THUMB_MAX_WIDTH,
100                     height=THUMB_MAX_HEIGHT
101                 )
102                 thumbnail_data = img.execute_transforms()
103                 thumbnail_key = key + THUMB_SUFFIX
104                 memcache.set(
105                     thumbnail_key,
106                     thumbnail_data,
107                     time=EXPIRATION_TIME
108                 )
109             except: #Failed to resize Image or add to memcache
110                 thumbnail_key = None
111         return (key, thumbnail_key)
112
113     def handle_upload(self):
114         results = []
115         for name, fieldStorage in self.request.POST.items():
116             if type(fieldStorage) is unicode:
117                 continue
118             result = {}
119             result['name'] = urllib.unquote(fieldStorage.filename)
120             result['type'] = fieldStorage.type
121             result['size'] = self.get_file_size(fieldStorage.file)
122             if self.validate(result):
123                 key, thumbnail_key = self.write_blob(
124                     fieldStorage.value,
125                     result
126                 )
127                 if key is not None:
128                     result['url'] = self.request.host_url + '/' + key
129                     result['deleteUrl'] = result['url']
130                     result['deleteType'] = 'DELETE'
131                     if thumbnail_key is not None:
132                         result['thumbnailUrl'] = self.request.host_url +\
133                              '/' + thumbnail_key
134                 else:
135                     result['error'] = 'Failed to store uploaded file.'
136             results.append(result)
137         return results
138
139     def head(self):
140         pass
141
142     def get(self):
143         self.redirect(WEBSITE)
144
145     def post(self):
146         if (self.request.get('_method') == 'DELETE'):
147             return self.delete()
148         result = {'files': self.handle_upload()}
149         s = self.json_stringify(result)
150         redirect = self.request.get('redirect')
151         if self.validate_redirect(redirect):
152             return self.redirect(str(
153                 redirect.replace('%s', urllib.quote(s, ''), 1)
154             ))
155         if 'application/json' in self.request.headers.get('Accept'):
156             self.response.headers['Content-Type'] = 'application/json'
157         self.response.write(s)
158
159 class FileHandler(CORSHandler):
160     def normalize(self, str):
161         return urllib.quote(urllib.unquote(str), '')
162
163     def get(self, content_type, data_hash, file_name):
164         content_type = self.normalize(content_type)
165         file_name = self.normalize(file_name)
166         key = content_type + '/' + data_hash + '/' + file_name
167         data = memcache.get(key)
168         if data is None:
169             return self.error(404)
170         # Prevent browsers from MIME-sniffing the content-type:
171         self.response.headers['X-Content-Type-Options'] = 'nosniff'
172         content_type = urllib.unquote(content_type)
173         if not IMAGE_TYPES.match(content_type):
174             # Force a download dialog for non-image types:
175             content_type = 'application/octet-stream'
176         elif file_name.endswith(THUMB_SUFFIX):
177             content_type = 'image/png'
178         self.response.headers['Content-Type'] = content_type
179         # Cache for the expiration time:
180         self.response.headers['Cache-Control'] = 'public,max-age=%d' \
181             % EXPIRATION_TIME
182         self.response.write(data)
183
184     def delete(self, content_type, data_hash, file_name):
185         content_type = self.normalize(content_type)
186         file_name = self.normalize(file_name)
187         key = content_type + '/' + data_hash + '/' + file_name
188         result = {key: memcache.delete(key)}
189         content_type = urllib.unquote(content_type)
190         if IMAGE_TYPES.match(content_type):
191             thumbnail_key = key + THUMB_SUFFIX
192             result[thumbnail_key] = memcache.delete(thumbnail_key)
193         if 'application/json' in self.request.headers.get('Accept'):
194             self.response.headers['Content-Type'] = 'application/json'
195         s = self.json_stringify(result)
196         self.response.write(s)
197
198 app = webapp2.WSGIApplication(
199     [
200         ('/', UploadHandler),
201         ('/(.+)/([^/]+)/([^/]+)', FileHandler)
202     ],
203     debug=DEBUG
204 )