card-mod-machines 0.11.5 → 0.11.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/vendor/jquery_file_upload/CONTRIBUTING.md +15 -0
  3. data/vendor/jquery_file_upload/LICENSE.txt +21 -0
  4. data/vendor/jquery_file_upload/README.md +107 -0
  5. data/vendor/jquery_file_upload/angularjs.html +211 -0
  6. data/vendor/jquery_file_upload/basic-plus.html +226 -0
  7. data/vendor/jquery_file_upload/basic.html +136 -0
  8. data/vendor/jquery_file_upload/bower-version-update.js +16 -0
  9. data/vendor/jquery_file_upload/bower.json +64 -0
  10. data/vendor/jquery_file_upload/cors/postmessage.html +75 -0
  11. data/vendor/jquery_file_upload/cors/result.html +24 -0
  12. data/vendor/jquery_file_upload/css/jquery-ui-demo-ie8.css +21 -0
  13. data/vendor/jquery_file_upload/css/jquery-ui-demo.css +67 -0
  14. data/vendor/jquery_file_upload/css/jquery.fileupload-noscript.css +22 -0
  15. data/vendor/jquery_file_upload/css/jquery.fileupload-ui-noscript.css +17 -0
  16. data/vendor/jquery_file_upload/css/jquery.fileupload-ui.css +57 -0
  17. data/vendor/jquery_file_upload/css/jquery.fileupload.css +37 -0
  18. data/vendor/jquery_file_upload/css/style.css +15 -0
  19. data/vendor/jquery_file_upload/img/loading.gif +0 -0
  20. data/vendor/jquery_file_upload/img/progressbar.gif +0 -0
  21. data/vendor/jquery_file_upload/index.html +255 -0
  22. data/vendor/jquery_file_upload/jquery-ui.html +252 -0
  23. data/vendor/jquery_file_upload/js/app.js +101 -0
  24. data/vendor/jquery_file_upload/js/cors/jquery.postmessage-transport.js +126 -0
  25. data/vendor/jquery_file_upload/js/cors/jquery.xdr-transport.js +89 -0
  26. data/vendor/jquery_file_upload/js/jquery.fileupload-angular.js +437 -0
  27. data/vendor/jquery_file_upload/js/jquery.fileupload-audio.js +113 -0
  28. data/vendor/jquery_file_upload/js/jquery.fileupload-image.js +326 -0
  29. data/vendor/jquery_file_upload/js/jquery.fileupload-jquery-ui.js +161 -0
  30. data/vendor/jquery_file_upload/js/jquery.fileupload-process.js +178 -0
  31. data/vendor/jquery_file_upload/js/jquery.fileupload-ui.js +714 -0
  32. data/vendor/jquery_file_upload/js/jquery.fileupload-validate.js +125 -0
  33. data/vendor/jquery_file_upload/js/jquery.fileupload-video.js +113 -0
  34. data/vendor/jquery_file_upload/js/jquery.fileupload.js +1486 -0
  35. data/vendor/jquery_file_upload/js/jquery.iframe-transport.js +224 -0
  36. data/vendor/jquery_file_upload/js/main.js +75 -0
  37. data/vendor/jquery_file_upload/js/vendor/jquery.ui.widget.js +748 -0
  38. data/vendor/jquery_file_upload/package.json +55 -0
  39. data/vendor/jquery_file_upload/server/gae-go/app.yaml +12 -0
  40. data/vendor/jquery_file_upload/server/gae-go/app/main.go +361 -0
  41. data/vendor/jquery_file_upload/server/gae-go/static/favicon.ico +0 -0
  42. data/vendor/jquery_file_upload/server/gae-go/static/robots.txt +2 -0
  43. data/vendor/jquery_file_upload/server/gae-python/app.yaml +17 -0
  44. data/vendor/jquery_file_upload/server/gae-python/main.py +204 -0
  45. data/vendor/jquery_file_upload/server/gae-python/static/favicon.ico +0 -0
  46. data/vendor/jquery_file_upload/server/gae-python/static/robots.txt +2 -0
  47. data/vendor/jquery_file_upload/server/php/Dockerfile +38 -0
  48. data/vendor/jquery_file_upload/server/php/UploadHandler.php +1411 -0
  49. data/vendor/jquery_file_upload/server/php/docker-compose.yml +9 -0
  50. data/vendor/jquery_file_upload/server/php/index.php +15 -0
  51. data/vendor/jquery_file_upload/test/index.html +172 -0
  52. data/vendor/jquery_file_upload/test/test.js +1292 -0
  53. data/vendor/jquery_rails/CHANGELOG.md +359 -0
  54. data/vendor/jquery_rails/CONTRIBUTING.md +132 -0
  55. data/vendor/jquery_rails/Gemfile +22 -0
  56. data/vendor/jquery_rails/MIT-LICENSE +21 -0
  57. data/vendor/jquery_rails/README.md +75 -0
  58. data/vendor/jquery_rails/Rakefile +59 -0
  59. data/vendor/jquery_rails/VERSIONS.md +62 -0
  60. data/vendor/jquery_rails/jquery-rails.gemspec +26 -0
  61. data/vendor/jquery_rails/lib/jquery-rails.rb +1 -0
  62. data/vendor/jquery_rails/lib/jquery/assert_select.rb +149 -0
  63. data/vendor/jquery_rails/lib/jquery/rails.rb +8 -0
  64. data/vendor/jquery_rails/lib/jquery/rails/engine.rb +6 -0
  65. data/vendor/jquery_rails/lib/jquery/rails/version.rb +9 -0
  66. data/vendor/jquery_rails/test/assert_select_jquery_test.rb +85 -0
  67. data/vendor/jquery_rails/test/test_helper.rb +6 -0
  68. data/vendor/jquery_rails/vendor/assets/javascripts/jquery.js +11008 -0
  69. data/vendor/jquery_rails/vendor/assets/javascripts/jquery.min.js +5 -0
  70. data/vendor/jquery_rails/vendor/assets/javascripts/jquery.min.map +1 -0
  71. data/vendor/jquery_rails/vendor/assets/javascripts/jquery2.js +9814 -0
  72. data/vendor/jquery_rails/vendor/assets/javascripts/jquery2.min.js +4 -0
  73. data/vendor/jquery_rails/vendor/assets/javascripts/jquery2.min.map +1 -0
  74. data/vendor/jquery_rails/vendor/assets/javascripts/jquery3.js +10364 -0
  75. data/vendor/jquery_rails/vendor/assets/javascripts/jquery3.min.js +2 -0
  76. data/vendor/jquery_rails/vendor/assets/javascripts/jquery3.min.map +1 -0
  77. data/vendor/jquery_rails/vendor/assets/javascripts/jquery_ujs.js +555 -0
  78. metadata +108 -12
@@ -0,0 +1,204 @@
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
+ )
@@ -0,0 +1,2 @@
1
+ User-agent: *
2
+ Disallow:
@@ -0,0 +1,38 @@
1
+ FROM php:7-apache
2
+
3
+ # Enable the Apache Headers module:
4
+ RUN ln -s /etc/apache2/mods-available/headers.load \
5
+ /etc/apache2/mods-enabled/headers.load
6
+
7
+ # Enable the Apache Rewrite module:
8
+ RUN ln -s /etc/apache2/mods-available/rewrite.load \
9
+ /etc/apache2/mods-enabled/rewrite.load
10
+
11
+ # Install GD, Imagick and ImageMagick as image conversion options:
12
+ RUN DEBIAN_FRONTEND=noninteractive \
13
+ apt-get update && apt-get install -y --no-install-recommends \
14
+ libpng-dev \
15
+ libjpeg-dev \
16
+ libmagickwand-dev \
17
+ imagemagick \
18
+ && pecl install \
19
+ imagick \
20
+ && docker-php-ext-enable \
21
+ imagick \
22
+ && docker-php-ext-configure \
23
+ gd --with-jpeg-dir=/usr/include/ \
24
+ && docker-php-ext-install \
25
+ gd \
26
+ # Uninstall obsolete packages:
27
+ && apt-get autoremove -y \
28
+ libpng-dev \
29
+ libjpeg-dev \
30
+ libmagickwand-dev \
31
+ # Remove obsolete files:
32
+ && apt-get clean \
33
+ && rm -rf \
34
+ /tmp/* \
35
+ /usr/share/doc/* \
36
+ /var/cache/* \
37
+ /var/lib/apt/lists/* \
38
+ /var/tmp/*
@@ -0,0 +1,1411 @@
1
+ <?php
2
+ /*
3
+ * jQuery File Upload Plugin PHP Class
4
+ * https://github.com/blueimp/jQuery-File-Upload
5
+ *
6
+ * Copyright 2010, Sebastian Tschan
7
+ * https://blueimp.net
8
+ *
9
+ * Licensed under the MIT license:
10
+ * https://opensource.org/licenses/MIT
11
+ */
12
+
13
+ class UploadHandler
14
+ {
15
+
16
+ protected $options;
17
+
18
+ // PHP File Upload error message codes:
19
+ // http://php.net/manual/en/features.file-upload.errors.php
20
+ protected $error_messages = array(
21
+ 1 => 'The uploaded file exceeds the upload_max_filesize directive in php.ini',
22
+ 2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
23
+ 3 => 'The uploaded file was only partially uploaded',
24
+ 4 => 'No file was uploaded',
25
+ 6 => 'Missing a temporary folder',
26
+ 7 => 'Failed to write file to disk',
27
+ 8 => 'A PHP extension stopped the file upload',
28
+ 'post_max_size' => 'The uploaded file exceeds the post_max_size directive in php.ini',
29
+ 'max_file_size' => 'File is too big',
30
+ 'min_file_size' => 'File is too small',
31
+ 'accept_file_types' => 'Filetype not allowed',
32
+ 'max_number_of_files' => 'Maximum number of files exceeded',
33
+ 'max_width' => 'Image exceeds maximum width',
34
+ 'min_width' => 'Image requires a minimum width',
35
+ 'max_height' => 'Image exceeds maximum height',
36
+ 'min_height' => 'Image requires a minimum height',
37
+ 'abort' => 'File upload aborted',
38
+ 'image_resize' => 'Failed to resize image'
39
+ );
40
+
41
+ protected $image_objects = array();
42
+
43
+ public function __construct($options = null, $initialize = true, $error_messages = null) {
44
+ $this->response = array();
45
+ $this->options = array(
46
+ 'script_url' => $this->get_full_url().'/'.$this->basename($this->get_server_var('SCRIPT_NAME')),
47
+ 'upload_dir' => dirname($this->get_server_var('SCRIPT_FILENAME')).'/files/',
48
+ 'upload_url' => $this->get_full_url().'/files/',
49
+ 'input_stream' => 'php://input',
50
+ 'user_dirs' => false,
51
+ 'mkdir_mode' => 0755,
52
+ 'param_name' => 'files',
53
+ // Set the following option to 'POST', if your server does not support
54
+ // DELETE requests. This is a parameter sent to the client:
55
+ 'delete_type' => 'DELETE',
56
+ 'access_control_allow_origin' => '*',
57
+ 'access_control_allow_credentials' => false,
58
+ 'access_control_allow_methods' => array(
59
+ 'OPTIONS',
60
+ 'HEAD',
61
+ 'GET',
62
+ 'POST',
63
+ 'PUT',
64
+ 'PATCH',
65
+ 'DELETE'
66
+ ),
67
+ 'access_control_allow_headers' => array(
68
+ 'Content-Type',
69
+ 'Content-Range',
70
+ 'Content-Disposition'
71
+ ),
72
+ // By default, allow redirects to the referer protocol+host:
73
+ 'redirect_allow_target' => '/^'.preg_quote(
74
+ parse_url($this->get_server_var('HTTP_REFERER'), PHP_URL_SCHEME)
75
+ .'://'
76
+ .parse_url($this->get_server_var('HTTP_REFERER'), PHP_URL_HOST)
77
+ .'/', // Trailing slash to not match subdomains by mistake
78
+ '/' // preg_quote delimiter param
79
+ ).'/',
80
+ // Enable to provide file downloads via GET requests to the PHP script:
81
+ // 1. Set to 1 to download files via readfile method through PHP
82
+ // 2. Set to 2 to send a X-Sendfile header for lighttpd/Apache
83
+ // 3. Set to 3 to send a X-Accel-Redirect header for nginx
84
+ // If set to 2 or 3, adjust the upload_url option to the base path of
85
+ // the redirect parameter, e.g. '/files/'.
86
+ 'download_via_php' => false,
87
+ // Read files in chunks to avoid memory limits when download_via_php
88
+ // is enabled, set to 0 to disable chunked reading of files:
89
+ 'readfile_chunk_size' => 10 * 1024 * 1024, // 10 MiB
90
+ // Defines which files can be displayed inline when downloaded:
91
+ 'inline_file_types' => '/\.(gif|jpe?g|png)$/i',
92
+ // Defines which files (based on their names) are accepted for upload:
93
+ 'accept_file_types' => '/.+$/i',
94
+ // The php.ini settings upload_max_filesize and post_max_size
95
+ // take precedence over the following max_file_size setting:
96
+ 'max_file_size' => null,
97
+ 'min_file_size' => 1,
98
+ // The maximum number of files for the upload directory:
99
+ 'max_number_of_files' => null,
100
+ // Defines which files are handled as image files:
101
+ 'image_file_types' => '/\.(gif|jpe?g|png)$/i',
102
+ // Use exif_imagetype on all files to correct file extensions:
103
+ 'correct_image_extensions' => false,
104
+ // Image resolution restrictions:
105
+ 'max_width' => null,
106
+ 'max_height' => null,
107
+ 'min_width' => 1,
108
+ 'min_height' => 1,
109
+ // Set the following option to false to enable resumable uploads:
110
+ 'discard_aborted_uploads' => true,
111
+ // Set to 0 to use the GD library to scale and orient images,
112
+ // set to 1 to use imagick (if installed, falls back to GD),
113
+ // set to 2 to use the ImageMagick convert binary directly:
114
+ 'image_library' => 1,
115
+ // Uncomment the following to define an array of resource limits
116
+ // for imagick:
117
+ /*
118
+ 'imagick_resource_limits' => array(
119
+ imagick::RESOURCETYPE_MAP => 32,
120
+ imagick::RESOURCETYPE_MEMORY => 32
121
+ ),
122
+ */
123
+ // Command or path for to the ImageMagick convert binary:
124
+ 'convert_bin' => 'convert',
125
+ // Uncomment the following to add parameters in front of each
126
+ // ImageMagick convert call (the limit constraints seem only
127
+ // to have an effect if put in front):
128
+ /*
129
+ 'convert_params' => '-limit memory 32MiB -limit map 32MiB',
130
+ */
131
+ // Command or path for to the ImageMagick identify binary:
132
+ 'identify_bin' => 'identify',
133
+ 'image_versions' => array(
134
+ // The empty image version key defines options for the original image.
135
+ // Keep in mind: these image manipulations are inherited by all other image versions from this point onwards.
136
+ // Also note that the property 'no_cache' is not inherited, since it's not a manipulation.
137
+ '' => array(
138
+ // Automatically rotate images based on EXIF meta data:
139
+ 'auto_orient' => true
140
+ ),
141
+ // You can add arrays to generate different versions.
142
+ // The name of the key is the name of the version (example: 'medium').
143
+ // the array contains the options to apply.
144
+ /*
145
+ 'medium' => array(
146
+ 'max_width' => 800,
147
+ 'max_height' => 600
148
+ ),
149
+ */
150
+ 'thumbnail' => array(
151
+ // Uncomment the following to use a defined directory for the thumbnails
152
+ // instead of a subdirectory based on the version identifier.
153
+ // Make sure that this directory doesn't allow execution of files if you
154
+ // don't pose any restrictions on the type of uploaded files, e.g. by
155
+ // copying the .htaccess file from the files directory for Apache:
156
+ //'upload_dir' => dirname($this->get_server_var('SCRIPT_FILENAME')).'/thumb/',
157
+ //'upload_url' => $this->get_full_url().'/thumb/',
158
+ // Uncomment the following to force the max
159
+ // dimensions and e.g. create square thumbnails:
160
+ // 'auto_orient' => true,
161
+ // 'crop' => true,
162
+ // 'jpeg_quality' => 70,
163
+ // 'no_cache' => true, (there's a caching option, but this remembers thumbnail sizes from a previous action!)
164
+ // 'strip' => true, (this strips EXIF tags, such as geolocation)
165
+ 'max_width' => 80, // either specify width, or set to 0. Then width is automatically adjusted - keeping aspect ratio to a specified max_height.
166
+ 'max_height' => 80 // either specify height, or set to 0. Then height is automatically adjusted - keeping aspect ratio to a specified max_width.
167
+ )
168
+ ),
169
+ 'print_response' => true
170
+ );
171
+ if ($options) {
172
+ $this->options = $options + $this->options;
173
+ }
174
+ if ($error_messages) {
175
+ $this->error_messages = $error_messages + $this->error_messages;
176
+ }
177
+ if ($initialize) {
178
+ $this->initialize();
179
+ }
180
+ }
181
+
182
+ protected function initialize() {
183
+ switch ($this->get_server_var('REQUEST_METHOD')) {
184
+ case 'OPTIONS':
185
+ case 'HEAD':
186
+ $this->head();
187
+ break;
188
+ case 'GET':
189
+ $this->get($this->options['print_response']);
190
+ break;
191
+ case 'PATCH':
192
+ case 'PUT':
193
+ case 'POST':
194
+ $this->post($this->options['print_response']);
195
+ break;
196
+ case 'DELETE':
197
+ $this->delete($this->options['print_response']);
198
+ break;
199
+ default:
200
+ $this->header('HTTP/1.1 405 Method Not Allowed');
201
+ }
202
+ }
203
+
204
+ protected function get_full_url() {
205
+ $https = !empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'on') === 0 ||
206
+ !empty($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
207
+ strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0;
208
+ return
209
+ ($https ? 'https://' : 'http://').
210
+ (!empty($_SERVER['REMOTE_USER']) ? $_SERVER['REMOTE_USER'].'@' : '').
211
+ (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ($_SERVER['SERVER_NAME'].
212
+ ($https && $_SERVER['SERVER_PORT'] === 443 ||
213
+ $_SERVER['SERVER_PORT'] === 80 ? '' : ':'.$_SERVER['SERVER_PORT']))).
214
+ substr($_SERVER['SCRIPT_NAME'],0, strrpos($_SERVER['SCRIPT_NAME'], '/'));
215
+ }
216
+
217
+ protected function get_user_id() {
218
+ @session_start();
219
+ return session_id();
220
+ }
221
+
222
+ protected function get_user_path() {
223
+ if ($this->options['user_dirs']) {
224
+ return $this->get_user_id().'/';
225
+ }
226
+ return '';
227
+ }
228
+
229
+ protected function get_upload_path($file_name = null, $version = null) {
230
+ $file_name = $file_name ? $file_name : '';
231
+ if (empty($version)) {
232
+ $version_path = '';
233
+ } else {
234
+ $version_dir = @$this->options['image_versions'][$version]['upload_dir'];
235
+ if ($version_dir) {
236
+ return $version_dir.$this->get_user_path().$file_name;
237
+ }
238
+ $version_path = $version.'/';
239
+ }
240
+ return $this->options['upload_dir'].$this->get_user_path()
241
+ .$version_path.$file_name;
242
+ }
243
+
244
+ protected function get_query_separator($url) {
245
+ return strpos($url, '?') === false ? '?' : '&';
246
+ }
247
+
248
+ protected function get_download_url($file_name, $version = null, $direct = false) {
249
+ if (!$direct && $this->options['download_via_php']) {
250
+ $url = $this->options['script_url']
251
+ .$this->get_query_separator($this->options['script_url'])
252
+ .$this->get_singular_param_name()
253
+ .'='.rawurlencode($file_name);
254
+ if ($version) {
255
+ $url .= '&version='.rawurlencode($version);
256
+ }
257
+ return $url.'&download=1';
258
+ }
259
+ if (empty($version)) {
260
+ $version_path = '';
261
+ } else {
262
+ $version_url = @$this->options['image_versions'][$version]['upload_url'];
263
+ if ($version_url) {
264
+ return $version_url.$this->get_user_path().rawurlencode($file_name);
265
+ }
266
+ $version_path = rawurlencode($version).'/';
267
+ }
268
+ return $this->options['upload_url'].$this->get_user_path()
269
+ .$version_path.rawurlencode($file_name);
270
+ }
271
+
272
+ protected function set_additional_file_properties($file) {
273
+ $file->deleteUrl = $this->options['script_url']
274
+ .$this->get_query_separator($this->options['script_url'])
275
+ .$this->get_singular_param_name()
276
+ .'='.rawurlencode($file->name);
277
+ $file->deleteType = $this->options['delete_type'];
278
+ if ($file->deleteType !== 'DELETE') {
279
+ $file->deleteUrl .= '&_method=DELETE';
280
+ }
281
+ if ($this->options['access_control_allow_credentials']) {
282
+ $file->deleteWithCredentials = true;
283
+ }
284
+ }
285
+
286
+ // Fix for overflowing signed 32 bit integers,
287
+ // works for sizes up to 2^32-1 bytes (4 GiB - 1):
288
+ protected function fix_integer_overflow($size) {
289
+ if ($size < 0) {
290
+ $size += 2.0 * (PHP_INT_MAX + 1);
291
+ }
292
+ return $size;
293
+ }
294
+
295
+ protected function get_file_size($file_path, $clear_stat_cache = false) {
296
+ if ($clear_stat_cache) {
297
+ if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
298
+ clearstatcache(true, $file_path);
299
+ } else {
300
+ clearstatcache();
301
+ }
302
+ }
303
+ return $this->fix_integer_overflow(filesize($file_path));
304
+ }
305
+
306
+ protected function is_valid_file_object($file_name) {
307
+ $file_path = $this->get_upload_path($file_name);
308
+ if (is_file($file_path) && $file_name[0] !== '.') {
309
+ return true;
310
+ }
311
+ return false;
312
+ }
313
+
314
+ protected function get_file_object($file_name) {
315
+ if ($this->is_valid_file_object($file_name)) {
316
+ $file = new \stdClass();
317
+ $file->name = $file_name;
318
+ $file->size = $this->get_file_size(
319
+ $this->get_upload_path($file_name)
320
+ );
321
+ $file->url = $this->get_download_url($file->name);
322
+ foreach ($this->options['image_versions'] as $version => $options) {
323
+ if (!empty($version)) {
324
+ if (is_file($this->get_upload_path($file_name, $version))) {
325
+ $file->{$version.'Url'} = $this->get_download_url(
326
+ $file->name,
327
+ $version
328
+ );
329
+ }
330
+ }
331
+ }
332
+ $this->set_additional_file_properties($file);
333
+ return $file;
334
+ }
335
+ return null;
336
+ }
337
+
338
+ protected function get_file_objects($iteration_method = 'get_file_object') {
339
+ $upload_dir = $this->get_upload_path();
340
+ if (!is_dir($upload_dir)) {
341
+ return array();
342
+ }
343
+ return array_values(array_filter(array_map(
344
+ array($this, $iteration_method),
345
+ scandir($upload_dir)
346
+ )));
347
+ }
348
+
349
+ protected function count_file_objects() {
350
+ return count($this->get_file_objects('is_valid_file_object'));
351
+ }
352
+
353
+ protected function get_error_message($error) {
354
+ return isset($this->error_messages[$error]) ?
355
+ $this->error_messages[$error] : $error;
356
+ }
357
+
358
+ public function get_config_bytes($val) {
359
+ $val = trim($val);
360
+ $last = strtolower($val[strlen($val)-1]);
361
+ $val = (int)$val;
362
+ switch ($last) {
363
+ case 'g':
364
+ $val *= 1024;
365
+ case 'm':
366
+ $val *= 1024;
367
+ case 'k':
368
+ $val *= 1024;
369
+ }
370
+ return $this->fix_integer_overflow($val);
371
+ }
372
+
373
+ protected function validate($uploaded_file, $file, $error, $index) {
374
+ if ($error) {
375
+ $file->error = $this->get_error_message($error);
376
+ return false;
377
+ }
378
+ $content_length = $this->fix_integer_overflow(
379
+ (int)$this->get_server_var('CONTENT_LENGTH')
380
+ );
381
+ $post_max_size = $this->get_config_bytes(ini_get('post_max_size'));
382
+ if ($post_max_size && ($content_length > $post_max_size)) {
383
+ $file->error = $this->get_error_message('post_max_size');
384
+ return false;
385
+ }
386
+ if (!preg_match($this->options['accept_file_types'], $file->name)) {
387
+ $file->error = $this->get_error_message('accept_file_types');
388
+ return false;
389
+ }
390
+ if ($uploaded_file && is_uploaded_file($uploaded_file)) {
391
+ $file_size = $this->get_file_size($uploaded_file);
392
+ } else {
393
+ $file_size = $content_length;
394
+ }
395
+ if ($this->options['max_file_size'] && (
396
+ $file_size > $this->options['max_file_size'] ||
397
+ $file->size > $this->options['max_file_size'])
398
+ ) {
399
+ $file->error = $this->get_error_message('max_file_size');
400
+ return false;
401
+ }
402
+ if ($this->options['min_file_size'] &&
403
+ $file_size < $this->options['min_file_size']) {
404
+ $file->error = $this->get_error_message('min_file_size');
405
+ return false;
406
+ }
407
+ if (is_int($this->options['max_number_of_files']) &&
408
+ ($this->count_file_objects() >= $this->options['max_number_of_files']) &&
409
+ // Ignore additional chunks of existing files:
410
+ !is_file($this->get_upload_path($file->name))) {
411
+ $file->error = $this->get_error_message('max_number_of_files');
412
+ return false;
413
+ }
414
+ $max_width = @$this->options['max_width'];
415
+ $max_height = @$this->options['max_height'];
416
+ $min_width = @$this->options['min_width'];
417
+ $min_height = @$this->options['min_height'];
418
+ if (($max_width || $max_height || $min_width || $min_height)
419
+ && preg_match($this->options['image_file_types'], $file->name)) {
420
+ list($img_width, $img_height) = $this->get_image_size($uploaded_file);
421
+
422
+ // If we are auto rotating the image by default, do the checks on
423
+ // the correct orientation
424
+ if (
425
+ @$this->options['image_versions']['']['auto_orient'] &&
426
+ function_exists('exif_read_data') &&
427
+ ($exif = @exif_read_data($uploaded_file)) &&
428
+ (((int) @$exif['Orientation']) >= 5)
429
+ ) {
430
+ $tmp = $img_width;
431
+ $img_width = $img_height;
432
+ $img_height = $tmp;
433
+ unset($tmp);
434
+ }
435
+
436
+ }
437
+ if (!empty($img_width)) {
438
+ if ($max_width && $img_width > $max_width) {
439
+ $file->error = $this->get_error_message('max_width');
440
+ return false;
441
+ }
442
+ if ($max_height && $img_height > $max_height) {
443
+ $file->error = $this->get_error_message('max_height');
444
+ return false;
445
+ }
446
+ if ($min_width && $img_width < $min_width) {
447
+ $file->error = $this->get_error_message('min_width');
448
+ return false;
449
+ }
450
+ if ($min_height && $img_height < $min_height) {
451
+ $file->error = $this->get_error_message('min_height');
452
+ return false;
453
+ }
454
+ }
455
+ return true;
456
+ }
457
+
458
+ protected function upcount_name_callback($matches) {
459
+ $index = isset($matches[1]) ? ((int)$matches[1]) + 1 : 1;
460
+ $ext = isset($matches[2]) ? $matches[2] : '';
461
+ return ' ('.$index.')'.$ext;
462
+ }
463
+
464
+ protected function upcount_name($name) {
465
+ return preg_replace_callback(
466
+ '/(?:(?: \(([\d]+)\))?(\.[^.]+))?$/',
467
+ array($this, 'upcount_name_callback'),
468
+ $name,
469
+ 1
470
+ );
471
+ }
472
+
473
+ protected function get_unique_filename($file_path, $name, $size, $type, $error,
474
+ $index, $content_range) {
475
+ while(is_dir($this->get_upload_path($name))) {
476
+ $name = $this->upcount_name($name);
477
+ }
478
+ // Keep an existing filename if this is part of a chunked upload:
479
+ $uploaded_bytes = $this->fix_integer_overflow((int)$content_range[1]);
480
+ while (is_file($this->get_upload_path($name))) {
481
+ if ($uploaded_bytes === $this->get_file_size(
482
+ $this->get_upload_path($name))) {
483
+ break;
484
+ }
485
+ $name = $this->upcount_name($name);
486
+ }
487
+ return $name;
488
+ }
489
+
490
+ protected function fix_file_extension($file_path, $name, $size, $type, $error,
491
+ $index, $content_range) {
492
+ // Add missing file extension for known image types:
493
+ if (strpos($name, '.') === false &&
494
+ preg_match('/^image\/(gif|jpe?g|png)/', $type, $matches)) {
495
+ $name .= '.'.$matches[1];
496
+ }
497
+ if ($this->options['correct_image_extensions'] &&
498
+ function_exists('exif_imagetype')) {
499
+ switch (@exif_imagetype($file_path)){
500
+ case IMAGETYPE_JPEG:
501
+ $extensions = array('jpg', 'jpeg');
502
+ break;
503
+ case IMAGETYPE_PNG:
504
+ $extensions = array('png');
505
+ break;
506
+ case IMAGETYPE_GIF:
507
+ $extensions = array('gif');
508
+ break;
509
+ }
510
+ // Adjust incorrect image file extensions:
511
+ if (!empty($extensions)) {
512
+ $parts = explode('.', $name);
513
+ $extIndex = count($parts) - 1;
514
+ $ext = strtolower(@$parts[$extIndex]);
515
+ if (!in_array($ext, $extensions)) {
516
+ $parts[$extIndex] = $extensions[0];
517
+ $name = implode('.', $parts);
518
+ }
519
+ }
520
+ }
521
+ return $name;
522
+ }
523
+
524
+ protected function trim_file_name($file_path, $name, $size, $type, $error,
525
+ $index, $content_range) {
526
+ // Remove path information and dots around the filename, to prevent uploading
527
+ // into different directories or replacing hidden system files.
528
+ // Also remove control characters and spaces (\x00..\x20) around the filename:
529
+ $name = trim($this->basename(stripslashes($name)), ".\x00..\x20");
530
+ // Use a timestamp for empty filenames:
531
+ if (!$name) {
532
+ $name = str_replace('.', '-', microtime(true));
533
+ }
534
+ return $name;
535
+ }
536
+
537
+ protected function get_file_name($file_path, $name, $size, $type, $error,
538
+ $index, $content_range) {
539
+ $name = $this->trim_file_name($file_path, $name, $size, $type, $error,
540
+ $index, $content_range);
541
+ return $this->get_unique_filename(
542
+ $file_path,
543
+ $this->fix_file_extension($file_path, $name, $size, $type, $error,
544
+ $index, $content_range),
545
+ $size,
546
+ $type,
547
+ $error,
548
+ $index,
549
+ $content_range
550
+ );
551
+ }
552
+
553
+ protected function get_scaled_image_file_paths($file_name, $version) {
554
+ $file_path = $this->get_upload_path($file_name);
555
+ if (!empty($version)) {
556
+ $version_dir = $this->get_upload_path(null, $version);
557
+ if (!is_dir($version_dir)) {
558
+ mkdir($version_dir, $this->options['mkdir_mode'], true);
559
+ }
560
+ $new_file_path = $version_dir.'/'.$file_name;
561
+ } else {
562
+ $new_file_path = $file_path;
563
+ }
564
+ return array($file_path, $new_file_path);
565
+ }
566
+
567
+ protected function gd_get_image_object($file_path, $func, $no_cache = false) {
568
+ if (empty($this->image_objects[$file_path]) || $no_cache) {
569
+ $this->gd_destroy_image_object($file_path);
570
+ $this->image_objects[$file_path] = $func($file_path);
571
+ }
572
+ return $this->image_objects[$file_path];
573
+ }
574
+
575
+ protected function gd_set_image_object($file_path, $image) {
576
+ $this->gd_destroy_image_object($file_path);
577
+ $this->image_objects[$file_path] = $image;
578
+ }
579
+
580
+ protected function gd_destroy_image_object($file_path) {
581
+ $image = (isset($this->image_objects[$file_path])) ? $this->image_objects[$file_path] : null ;
582
+ return $image && imagedestroy($image);
583
+ }
584
+
585
+ protected function gd_imageflip($image, $mode) {
586
+ if (function_exists('imageflip')) {
587
+ return imageflip($image, $mode);
588
+ }
589
+ $new_width = $src_width = imagesx($image);
590
+ $new_height = $src_height = imagesy($image);
591
+ $new_img = imagecreatetruecolor($new_width, $new_height);
592
+ $src_x = 0;
593
+ $src_y = 0;
594
+ switch ($mode) {
595
+ case '1': // flip on the horizontal axis
596
+ $src_y = $new_height - 1;
597
+ $src_height = -$new_height;
598
+ break;
599
+ case '2': // flip on the vertical axis
600
+ $src_x = $new_width - 1;
601
+ $src_width = -$new_width;
602
+ break;
603
+ case '3': // flip on both axes
604
+ $src_y = $new_height - 1;
605
+ $src_height = -$new_height;
606
+ $src_x = $new_width - 1;
607
+ $src_width = -$new_width;
608
+ break;
609
+ default:
610
+ return $image;
611
+ }
612
+ imagecopyresampled(
613
+ $new_img,
614
+ $image,
615
+ 0,
616
+ 0,
617
+ $src_x,
618
+ $src_y,
619
+ $new_width,
620
+ $new_height,
621
+ $src_width,
622
+ $src_height
623
+ );
624
+ return $new_img;
625
+ }
626
+
627
+ protected function gd_orient_image($file_path, $src_img) {
628
+ if (!function_exists('exif_read_data')) {
629
+ return false;
630
+ }
631
+ $exif = @exif_read_data($file_path);
632
+ if ($exif === false) {
633
+ return false;
634
+ }
635
+ $orientation = (int)@$exif['Orientation'];
636
+ if ($orientation < 2 || $orientation > 8) {
637
+ return false;
638
+ }
639
+ switch ($orientation) {
640
+ case 2:
641
+ $new_img = $this->gd_imageflip(
642
+ $src_img,
643
+ defined('IMG_FLIP_VERTICAL') ? IMG_FLIP_VERTICAL : 2
644
+ );
645
+ break;
646
+ case 3:
647
+ $new_img = imagerotate($src_img, 180, 0);
648
+ break;
649
+ case 4:
650
+ $new_img = $this->gd_imageflip(
651
+ $src_img,
652
+ defined('IMG_FLIP_HORIZONTAL') ? IMG_FLIP_HORIZONTAL : 1
653
+ );
654
+ break;
655
+ case 5:
656
+ $tmp_img = $this->gd_imageflip(
657
+ $src_img,
658
+ defined('IMG_FLIP_HORIZONTAL') ? IMG_FLIP_HORIZONTAL : 1
659
+ );
660
+ $new_img = imagerotate($tmp_img, 270, 0);
661
+ imagedestroy($tmp_img);
662
+ break;
663
+ case 6:
664
+ $new_img = imagerotate($src_img, 270, 0);
665
+ break;
666
+ case 7:
667
+ $tmp_img = $this->gd_imageflip(
668
+ $src_img,
669
+ defined('IMG_FLIP_VERTICAL') ? IMG_FLIP_VERTICAL : 2
670
+ );
671
+ $new_img = imagerotate($tmp_img, 270, 0);
672
+ imagedestroy($tmp_img);
673
+ break;
674
+ case 8:
675
+ $new_img = imagerotate($src_img, 90, 0);
676
+ break;
677
+ default:
678
+ return false;
679
+ }
680
+ $this->gd_set_image_object($file_path, $new_img);
681
+ return true;
682
+ }
683
+
684
+ protected function gd_create_scaled_image($file_name, $version, $options) {
685
+ if (!function_exists('imagecreatetruecolor')) {
686
+ error_log('Function not found: imagecreatetruecolor');
687
+ return false;
688
+ }
689
+ list($file_path, $new_file_path) =
690
+ $this->get_scaled_image_file_paths($file_name, $version);
691
+ $type = strtolower(substr(strrchr($file_name, '.'), 1));
692
+ switch ($type) {
693
+ case 'jpg':
694
+ case 'jpeg':
695
+ $src_func = 'imagecreatefromjpeg';
696
+ $write_func = 'imagejpeg';
697
+ $image_quality = isset($options['jpeg_quality']) ?
698
+ $options['jpeg_quality'] : 75;
699
+ break;
700
+ case 'gif':
701
+ $src_func = 'imagecreatefromgif';
702
+ $write_func = 'imagegif';
703
+ $image_quality = null;
704
+ break;
705
+ case 'png':
706
+ $src_func = 'imagecreatefrompng';
707
+ $write_func = 'imagepng';
708
+ $image_quality = isset($options['png_quality']) ?
709
+ $options['png_quality'] : 9;
710
+ break;
711
+ default:
712
+ return false;
713
+ }
714
+ $src_img = $this->gd_get_image_object(
715
+ $file_path,
716
+ $src_func,
717
+ !empty($options['no_cache'])
718
+ );
719
+ $image_oriented = false;
720
+ if (!empty($options['auto_orient']) && $this->gd_orient_image(
721
+ $file_path,
722
+ $src_img
723
+ )) {
724
+ $image_oriented = true;
725
+ $src_img = $this->gd_get_image_object(
726
+ $file_path,
727
+ $src_func
728
+ );
729
+ }
730
+ $max_width = $img_width = imagesx($src_img);
731
+ $max_height = $img_height = imagesy($src_img);
732
+ if (!empty($options['max_width'])) {
733
+ $max_width = $options['max_width'];
734
+ }
735
+ if (!empty($options['max_height'])) {
736
+ $max_height = $options['max_height'];
737
+ }
738
+ $scale = min(
739
+ $max_width / $img_width,
740
+ $max_height / $img_height
741
+ );
742
+ if ($scale >= 1) {
743
+ if ($image_oriented) {
744
+ return $write_func($src_img, $new_file_path, $image_quality);
745
+ }
746
+ if ($file_path !== $new_file_path) {
747
+ return copy($file_path, $new_file_path);
748
+ }
749
+ return true;
750
+ }
751
+ if (empty($options['crop'])) {
752
+ $new_width = $img_width * $scale;
753
+ $new_height = $img_height * $scale;
754
+ $dst_x = 0;
755
+ $dst_y = 0;
756
+ $new_img = imagecreatetruecolor($new_width, $new_height);
757
+ } else {
758
+ if (($img_width / $img_height) >= ($max_width / $max_height)) {
759
+ $new_width = $img_width / ($img_height / $max_height);
760
+ $new_height = $max_height;
761
+ } else {
762
+ $new_width = $max_width;
763
+ $new_height = $img_height / ($img_width / $max_width);
764
+ }
765
+ $dst_x = 0 - ($new_width - $max_width) / 2;
766
+ $dst_y = 0 - ($new_height - $max_height) / 2;
767
+ $new_img = imagecreatetruecolor($max_width, $max_height);
768
+ }
769
+ // Handle transparency in GIF and PNG images:
770
+ switch ($type) {
771
+ case 'gif':
772
+ case 'png':
773
+ imagecolortransparent($new_img, imagecolorallocate($new_img, 0, 0, 0));
774
+ case 'png':
775
+ imagealphablending($new_img, false);
776
+ imagesavealpha($new_img, true);
777
+ break;
778
+ }
779
+ $success = imagecopyresampled(
780
+ $new_img,
781
+ $src_img,
782
+ $dst_x,
783
+ $dst_y,
784
+ 0,
785
+ 0,
786
+ $new_width,
787
+ $new_height,
788
+ $img_width,
789
+ $img_height
790
+ ) && $write_func($new_img, $new_file_path, $image_quality);
791
+ $this->gd_set_image_object($file_path, $new_img);
792
+ return $success;
793
+ }
794
+
795
+ protected function imagick_get_image_object($file_path, $no_cache = false) {
796
+ if (empty($this->image_objects[$file_path]) || $no_cache) {
797
+ $this->imagick_destroy_image_object($file_path);
798
+ $image = new \Imagick();
799
+ if (!empty($this->options['imagick_resource_limits'])) {
800
+ foreach ($this->options['imagick_resource_limits'] as $type => $limit) {
801
+ $image->setResourceLimit($type, $limit);
802
+ }
803
+ }
804
+ $image->readImage($file_path);
805
+ $this->image_objects[$file_path] = $image;
806
+ }
807
+ return $this->image_objects[$file_path];
808
+ }
809
+
810
+ protected function imagick_set_image_object($file_path, $image) {
811
+ $this->imagick_destroy_image_object($file_path);
812
+ $this->image_objects[$file_path] = $image;
813
+ }
814
+
815
+ protected function imagick_destroy_image_object($file_path) {
816
+ $image = (isset($this->image_objects[$file_path])) ? $this->image_objects[$file_path] : null ;
817
+ return $image && $image->destroy();
818
+ }
819
+
820
+ protected function imagick_orient_image($image) {
821
+ $orientation = $image->getImageOrientation();
822
+ $background = new \ImagickPixel('none');
823
+ switch ($orientation) {
824
+ case \imagick::ORIENTATION_TOPRIGHT: // 2
825
+ $image->flopImage(); // horizontal flop around y-axis
826
+ break;
827
+ case \imagick::ORIENTATION_BOTTOMRIGHT: // 3
828
+ $image->rotateImage($background, 180);
829
+ break;
830
+ case \imagick::ORIENTATION_BOTTOMLEFT: // 4
831
+ $image->flipImage(); // vertical flip around x-axis
832
+ break;
833
+ case \imagick::ORIENTATION_LEFTTOP: // 5
834
+ $image->flopImage(); // horizontal flop around y-axis
835
+ $image->rotateImage($background, 270);
836
+ break;
837
+ case \imagick::ORIENTATION_RIGHTTOP: // 6
838
+ $image->rotateImage($background, 90);
839
+ break;
840
+ case \imagick::ORIENTATION_RIGHTBOTTOM: // 7
841
+ $image->flipImage(); // vertical flip around x-axis
842
+ $image->rotateImage($background, 270);
843
+ break;
844
+ case \imagick::ORIENTATION_LEFTBOTTOM: // 8
845
+ $image->rotateImage($background, 270);
846
+ break;
847
+ default:
848
+ return false;
849
+ }
850
+ $image->setImageOrientation(\imagick::ORIENTATION_TOPLEFT); // 1
851
+ return true;
852
+ }
853
+
854
+ protected function imagick_create_scaled_image($file_name, $version, $options) {
855
+ list($file_path, $new_file_path) =
856
+ $this->get_scaled_image_file_paths($file_name, $version);
857
+ $image = $this->imagick_get_image_object(
858
+ $file_path,
859
+ !empty($options['crop']) || !empty($options['no_cache'])
860
+ );
861
+ if ($image->getImageFormat() === 'GIF') {
862
+ // Handle animated GIFs:
863
+ $images = $image->coalesceImages();
864
+ foreach ($images as $frame) {
865
+ $image = $frame;
866
+ $this->imagick_set_image_object($file_name, $image);
867
+ break;
868
+ }
869
+ }
870
+ $image_oriented = false;
871
+ if (!empty($options['auto_orient'])) {
872
+ $image_oriented = $this->imagick_orient_image($image);
873
+ }
874
+
875
+ $image_resize = false;
876
+ $new_width = $max_width = $img_width = $image->getImageWidth();
877
+ $new_height = $max_height = $img_height = $image->getImageHeight();
878
+
879
+ // use isset(). User might be setting max_width = 0 (auto in regular resizing). Value 0 would be considered empty when you use empty()
880
+ if (isset($options['max_width'])) {
881
+ $image_resize = true;
882
+ $new_width = $max_width = $options['max_width'];
883
+ }
884
+ if (isset($options['max_height'])) {
885
+ $image_resize = true;
886
+ $new_height = $max_height = $options['max_height'];
887
+ }
888
+
889
+ $image_strip = (isset($options['strip']) ? $options['strip'] : false);
890
+
891
+ if ( !$image_oriented && ($max_width >= $img_width) && ($max_height >= $img_height) && !$image_strip && empty($options["jpeg_quality"]) ) {
892
+ if ($file_path !== $new_file_path) {
893
+ return copy($file_path, $new_file_path);
894
+ }
895
+ return true;
896
+ }
897
+ $crop = (isset($options['crop']) ? $options['crop'] : false);
898
+
899
+ if ($crop) {
900
+ $x = 0;
901
+ $y = 0;
902
+ if (($img_width / $img_height) >= ($max_width / $max_height)) {
903
+ $new_width = 0; // Enables proportional scaling based on max_height
904
+ $x = ($img_width / ($img_height / $max_height) - $max_width) / 2;
905
+ } else {
906
+ $new_height = 0; // Enables proportional scaling based on max_width
907
+ $y = ($img_height / ($img_width / $max_width) - $max_height) / 2;
908
+ }
909
+ }
910
+ $success = $image->resizeImage(
911
+ $new_width,
912
+ $new_height,
913
+ isset($options['filter']) ? $options['filter'] : \imagick::FILTER_LANCZOS,
914
+ isset($options['blur']) ? $options['blur'] : 1,
915
+ $new_width && $new_height // fit image into constraints if not to be cropped
916
+ );
917
+ if ($success && $crop) {
918
+ $success = $image->cropImage(
919
+ $max_width,
920
+ $max_height,
921
+ $x,
922
+ $y
923
+ );
924
+ if ($success) {
925
+ $success = $image->setImagePage($max_width, $max_height, 0, 0);
926
+ }
927
+ }
928
+ $type = strtolower(substr(strrchr($file_name, '.'), 1));
929
+ switch ($type) {
930
+ case 'jpg':
931
+ case 'jpeg':
932
+ if (!empty($options['jpeg_quality'])) {
933
+ $image->setImageCompression(\imagick::COMPRESSION_JPEG);
934
+ $image->setImageCompressionQuality($options['jpeg_quality']);
935
+ }
936
+ break;
937
+ }
938
+ if ( $image_strip ) {
939
+ $image->stripImage();
940
+ }
941
+ return $success && $image->writeImage($new_file_path);
942
+ }
943
+
944
+ protected function imagemagick_create_scaled_image($file_name, $version, $options) {
945
+ list($file_path, $new_file_path) =
946
+ $this->get_scaled_image_file_paths($file_name, $version);
947
+ $resize = @$options['max_width']
948
+ .(empty($options['max_height']) ? '' : 'X'.$options['max_height']);
949
+ if (!$resize && empty($options['auto_orient'])) {
950
+ if ($file_path !== $new_file_path) {
951
+ return copy($file_path, $new_file_path);
952
+ }
953
+ return true;
954
+ }
955
+ $cmd = $this->options['convert_bin'];
956
+ if (!empty($this->options['convert_params'])) {
957
+ $cmd .= ' '.$this->options['convert_params'];
958
+ }
959
+ $cmd .= ' '.escapeshellarg($file_path);
960
+ if (!empty($options['auto_orient'])) {
961
+ $cmd .= ' -auto-orient';
962
+ }
963
+ if ($resize) {
964
+ // Handle animated GIFs:
965
+ $cmd .= ' -coalesce';
966
+ if (empty($options['crop'])) {
967
+ $cmd .= ' -resize '.escapeshellarg($resize.'>');
968
+ } else {
969
+ $cmd .= ' -resize '.escapeshellarg($resize.'^');
970
+ $cmd .= ' -gravity center';
971
+ $cmd .= ' -crop '.escapeshellarg($resize.'+0+0');
972
+ }
973
+ // Make sure the page dimensions are correct (fixes offsets of animated GIFs):
974
+ $cmd .= ' +repage';
975
+ }
976
+ if (!empty($options['convert_params'])) {
977
+ $cmd .= ' '.$options['convert_params'];
978
+ }
979
+ $cmd .= ' '.escapeshellarg($new_file_path);
980
+ exec($cmd, $output, $error);
981
+ if ($error) {
982
+ error_log(implode('\n', $output));
983
+ return false;
984
+ }
985
+ return true;
986
+ }
987
+
988
+ protected function get_image_size($file_path) {
989
+ if ($this->options['image_library']) {
990
+ if (extension_loaded('imagick')) {
991
+ $image = new \Imagick();
992
+ try {
993
+ if (@$image->pingImage($file_path)) {
994
+ $dimensions = array($image->getImageWidth(), $image->getImageHeight());
995
+ $image->destroy();
996
+ return $dimensions;
997
+ }
998
+ return false;
999
+ } catch (\Exception $e) {
1000
+ error_log($e->getMessage());
1001
+ }
1002
+ }
1003
+ if ($this->options['image_library'] === 2) {
1004
+ $cmd = $this->options['identify_bin'];
1005
+ $cmd .= ' -ping '.escapeshellarg($file_path);
1006
+ exec($cmd, $output, $error);
1007
+ if (!$error && !empty($output)) {
1008
+ // image.jpg JPEG 1920x1080 1920x1080+0+0 8-bit sRGB 465KB 0.000u 0:00.000
1009
+ $infos = preg_split('/\s+/', substr($output[0], strlen($file_path)));
1010
+ $dimensions = preg_split('/x/', $infos[2]);
1011
+ return $dimensions;
1012
+ }
1013
+ return false;
1014
+ }
1015
+ }
1016
+ if (!function_exists('getimagesize')) {
1017
+ error_log('Function not found: getimagesize');
1018
+ return false;
1019
+ }
1020
+ return @getimagesize($file_path);
1021
+ }
1022
+
1023
+ protected function create_scaled_image($file_name, $version, $options) {
1024
+ if ($this->options['image_library'] === 2) {
1025
+ return $this->imagemagick_create_scaled_image($file_name, $version, $options);
1026
+ }
1027
+ if ($this->options['image_library'] && extension_loaded('imagick')) {
1028
+ return $this->imagick_create_scaled_image($file_name, $version, $options);
1029
+ }
1030
+ return $this->gd_create_scaled_image($file_name, $version, $options);
1031
+ }
1032
+
1033
+ protected function destroy_image_object($file_path) {
1034
+ if ($this->options['image_library'] && extension_loaded('imagick')) {
1035
+ return $this->imagick_destroy_image_object($file_path);
1036
+ }
1037
+ }
1038
+
1039
+ protected function is_valid_image_file($file_path) {
1040
+ if (!preg_match($this->options['image_file_types'], $file_path)) {
1041
+ return false;
1042
+ }
1043
+ if (function_exists('exif_imagetype')) {
1044
+ return @exif_imagetype($file_path);
1045
+ }
1046
+ $image_info = $this->get_image_size($file_path);
1047
+ return $image_info && $image_info[0] && $image_info[1];
1048
+ }
1049
+
1050
+ protected function handle_image_file($file_path, $file) {
1051
+ $failed_versions = array();
1052
+ foreach ($this->options['image_versions'] as $version => $options) {
1053
+ if ($this->create_scaled_image($file->name, $version, $options)) {
1054
+ if (!empty($version)) {
1055
+ $file->{$version.'Url'} = $this->get_download_url(
1056
+ $file->name,
1057
+ $version
1058
+ );
1059
+ } else {
1060
+ $file->size = $this->get_file_size($file_path, true);
1061
+ }
1062
+ } else {
1063
+ $failed_versions[] = $version ? $version : 'original';
1064
+ }
1065
+ }
1066
+ if (count($failed_versions)) {
1067
+ $file->error = $this->get_error_message('image_resize')
1068
+ .' ('.implode($failed_versions, ', ').')';
1069
+ }
1070
+ // Free memory:
1071
+ $this->destroy_image_object($file_path);
1072
+ }
1073
+
1074
+ protected function handle_file_upload($uploaded_file, $name, $size, $type, $error,
1075
+ $index = null, $content_range = null) {
1076
+ $file = new \stdClass();
1077
+ $file->name = $this->get_file_name($uploaded_file, $name, $size, $type, $error,
1078
+ $index, $content_range);
1079
+ $file->size = $this->fix_integer_overflow((int)$size);
1080
+ $file->type = $type;
1081
+ if ($this->validate($uploaded_file, $file, $error, $index)) {
1082
+ $this->handle_form_data($file, $index);
1083
+ $upload_dir = $this->get_upload_path();
1084
+ if (!is_dir($upload_dir)) {
1085
+ mkdir($upload_dir, $this->options['mkdir_mode'], true);
1086
+ }
1087
+ $file_path = $this->get_upload_path($file->name);
1088
+ $append_file = $content_range && is_file($file_path) &&
1089
+ $file->size > $this->get_file_size($file_path);
1090
+ if ($uploaded_file && is_uploaded_file($uploaded_file)) {
1091
+ // multipart/formdata uploads (POST method uploads)
1092
+ if ($append_file) {
1093
+ file_put_contents(
1094
+ $file_path,
1095
+ fopen($uploaded_file, 'r'),
1096
+ FILE_APPEND
1097
+ );
1098
+ } else {
1099
+ move_uploaded_file($uploaded_file, $file_path);
1100
+ }
1101
+ } else {
1102
+ // Non-multipart uploads (PUT method support)
1103
+ file_put_contents(
1104
+ $file_path,
1105
+ fopen($this->options['input_stream'], 'r'),
1106
+ $append_file ? FILE_APPEND : 0
1107
+ );
1108
+ }
1109
+ $file_size = $this->get_file_size($file_path, $append_file);
1110
+ if ($file_size === $file->size) {
1111
+ $file->url = $this->get_download_url($file->name);
1112
+ if ($this->is_valid_image_file($file_path)) {
1113
+ $this->handle_image_file($file_path, $file);
1114
+ }
1115
+ } else {
1116
+ $file->size = $file_size;
1117
+ if (!$content_range && $this->options['discard_aborted_uploads']) {
1118
+ unlink($file_path);
1119
+ $file->error = $this->get_error_message('abort');
1120
+ }
1121
+ }
1122
+ $this->set_additional_file_properties($file);
1123
+ }
1124
+ return $file;
1125
+ }
1126
+
1127
+ protected function readfile($file_path) {
1128
+ $file_size = $this->get_file_size($file_path);
1129
+ $chunk_size = $this->options['readfile_chunk_size'];
1130
+ if ($chunk_size && $file_size > $chunk_size) {
1131
+ $handle = fopen($file_path, 'rb');
1132
+ while (!feof($handle)) {
1133
+ echo fread($handle, $chunk_size);
1134
+ @ob_flush();
1135
+ @flush();
1136
+ }
1137
+ fclose($handle);
1138
+ return $file_size;
1139
+ }
1140
+ return readfile($file_path);
1141
+ }
1142
+
1143
+ protected function body($str) {
1144
+ echo $str;
1145
+ }
1146
+
1147
+ protected function header($str) {
1148
+ header($str);
1149
+ }
1150
+
1151
+ protected function get_upload_data($id) {
1152
+ return @$_FILES[$id];
1153
+ }
1154
+
1155
+ protected function get_post_param($id) {
1156
+ return @$_POST[$id];
1157
+ }
1158
+
1159
+ protected function get_query_param($id) {
1160
+ return @$_GET[$id];
1161
+ }
1162
+
1163
+ protected function get_server_var($id) {
1164
+ return @$_SERVER[$id];
1165
+ }
1166
+
1167
+ protected function handle_form_data($file, $index) {
1168
+ // Handle form data, e.g. $_POST['description'][$index]
1169
+ }
1170
+
1171
+ protected function get_version_param() {
1172
+ return $this->basename(stripslashes($this->get_query_param('version')));
1173
+ }
1174
+
1175
+ protected function get_singular_param_name() {
1176
+ return substr($this->options['param_name'], 0, -1);
1177
+ }
1178
+
1179
+ protected function get_file_name_param() {
1180
+ $name = $this->get_singular_param_name();
1181
+ return $this->basename(stripslashes($this->get_query_param($name)));
1182
+ }
1183
+
1184
+ protected function get_file_names_params() {
1185
+ $params = $this->get_query_param($this->options['param_name']);
1186
+ if (!$params) {
1187
+ return null;
1188
+ }
1189
+ foreach ($params as $key => $value) {
1190
+ $params[$key] = $this->basename(stripslashes($value));
1191
+ }
1192
+ return $params;
1193
+ }
1194
+
1195
+ protected function get_file_type($file_path) {
1196
+ switch (strtolower(pathinfo($file_path, PATHINFO_EXTENSION))) {
1197
+ case 'jpeg':
1198
+ case 'jpg':
1199
+ return 'image/jpeg';
1200
+ case 'png':
1201
+ return 'image/png';
1202
+ case 'gif':
1203
+ return 'image/gif';
1204
+ default:
1205
+ return '';
1206
+ }
1207
+ }
1208
+
1209
+ protected function download() {
1210
+ switch ($this->options['download_via_php']) {
1211
+ case 1:
1212
+ $redirect_header = null;
1213
+ break;
1214
+ case 2:
1215
+ $redirect_header = 'X-Sendfile';
1216
+ break;
1217
+ case 3:
1218
+ $redirect_header = 'X-Accel-Redirect';
1219
+ break;
1220
+ default:
1221
+ return $this->header('HTTP/1.1 403 Forbidden');
1222
+ }
1223
+ $file_name = $this->get_file_name_param();
1224
+ if (!$this->is_valid_file_object($file_name)) {
1225
+ return $this->header('HTTP/1.1 404 Not Found');
1226
+ }
1227
+ if ($redirect_header) {
1228
+ return $this->header(
1229
+ $redirect_header.': '.$this->get_download_url(
1230
+ $file_name,
1231
+ $this->get_version_param(),
1232
+ true
1233
+ )
1234
+ );
1235
+ }
1236
+ $file_path = $this->get_upload_path($file_name, $this->get_version_param());
1237
+ // Prevent browsers from MIME-sniffing the content-type:
1238
+ $this->header('X-Content-Type-Options: nosniff');
1239
+ if (!preg_match($this->options['inline_file_types'], $file_name)) {
1240
+ $this->header('Content-Type: application/octet-stream');
1241
+ $this->header('Content-Disposition: attachment; filename="'.$file_name.'"');
1242
+ } else {
1243
+ $this->header('Content-Type: '.$this->get_file_type($file_path));
1244
+ $this->header('Content-Disposition: inline; filename="'.$file_name.'"');
1245
+ }
1246
+ $this->header('Content-Length: '.$this->get_file_size($file_path));
1247
+ $this->header('Last-Modified: '.gmdate('D, d M Y H:i:s T', filemtime($file_path)));
1248
+ $this->readfile($file_path);
1249
+ }
1250
+
1251
+ protected function send_content_type_header() {
1252
+ $this->header('Vary: Accept');
1253
+ if (strpos($this->get_server_var('HTTP_ACCEPT'), 'application/json') !== false) {
1254
+ $this->header('Content-type: application/json');
1255
+ } else {
1256
+ $this->header('Content-type: text/plain');
1257
+ }
1258
+ }
1259
+
1260
+ protected function send_access_control_headers() {
1261
+ $this->header('Access-Control-Allow-Origin: '.$this->options['access_control_allow_origin']);
1262
+ $this->header('Access-Control-Allow-Credentials: '
1263
+ .($this->options['access_control_allow_credentials'] ? 'true' : 'false'));
1264
+ $this->header('Access-Control-Allow-Methods: '
1265
+ .implode(', ', $this->options['access_control_allow_methods']));
1266
+ $this->header('Access-Control-Allow-Headers: '
1267
+ .implode(', ', $this->options['access_control_allow_headers']));
1268
+ }
1269
+
1270
+ public function generate_response($content, $print_response = true) {
1271
+ $this->response = $content;
1272
+ if ($print_response) {
1273
+ $json = json_encode($content);
1274
+ $redirect = stripslashes($this->get_post_param('redirect'));
1275
+ if ($redirect && preg_match($this->options['redirect_allow_target'], $redirect)) {
1276
+ $this->header('Location: '.sprintf($redirect, rawurlencode($json)));
1277
+ return;
1278
+ }
1279
+ $this->head();
1280
+ if ($this->get_server_var('HTTP_CONTENT_RANGE')) {
1281
+ $files = isset($content[$this->options['param_name']]) ?
1282
+ $content[$this->options['param_name']] : null;
1283
+ if ($files && is_array($files) && is_object($files[0]) && $files[0]->size) {
1284
+ $this->header('Range: 0-'.(
1285
+ $this->fix_integer_overflow((int)$files[0]->size) - 1
1286
+ ));
1287
+ }
1288
+ }
1289
+ $this->body($json);
1290
+ }
1291
+ return $content;
1292
+ }
1293
+
1294
+ public function get_response () {
1295
+ return $this->response;
1296
+ }
1297
+
1298
+ public function head() {
1299
+ $this->header('Pragma: no-cache');
1300
+ $this->header('Cache-Control: no-store, no-cache, must-revalidate');
1301
+ $this->header('Content-Disposition: inline; filename="files.json"');
1302
+ // Prevent Internet Explorer from MIME-sniffing the content-type:
1303
+ $this->header('X-Content-Type-Options: nosniff');
1304
+ if ($this->options['access_control_allow_origin']) {
1305
+ $this->send_access_control_headers();
1306
+ }
1307
+ $this->send_content_type_header();
1308
+ }
1309
+
1310
+ public function get($print_response = true) {
1311
+ if ($print_response && $this->get_query_param('download')) {
1312
+ return $this->download();
1313
+ }
1314
+ $file_name = $this->get_file_name_param();
1315
+ if ($file_name) {
1316
+ $response = array(
1317
+ $this->get_singular_param_name() => $this->get_file_object($file_name)
1318
+ );
1319
+ } else {
1320
+ $response = array(
1321
+ $this->options['param_name'] => $this->get_file_objects()
1322
+ );
1323
+ }
1324
+ return $this->generate_response($response, $print_response);
1325
+ }
1326
+
1327
+ public function post($print_response = true) {
1328
+ if ($this->get_query_param('_method') === 'DELETE') {
1329
+ return $this->delete($print_response);
1330
+ }
1331
+ $upload = $this->get_upload_data($this->options['param_name']);
1332
+ // Parse the Content-Disposition header, if available:
1333
+ $content_disposition_header = $this->get_server_var('HTTP_CONTENT_DISPOSITION');
1334
+ $file_name = $content_disposition_header ?
1335
+ rawurldecode(preg_replace(
1336
+ '/(^[^"]+")|("$)/',
1337
+ '',
1338
+ $content_disposition_header
1339
+ )) : null;
1340
+ // Parse the Content-Range header, which has the following form:
1341
+ // Content-Range: bytes 0-524287/2000000
1342
+ $content_range_header = $this->get_server_var('HTTP_CONTENT_RANGE');
1343
+ $content_range = $content_range_header ?
1344
+ preg_split('/[^0-9]+/', $content_range_header) : null;
1345
+ $size = $content_range ? $content_range[3] : null;
1346
+ $files = array();
1347
+ if ($upload) {
1348
+ if (is_array($upload['tmp_name'])) {
1349
+ // param_name is an array identifier like "files[]",
1350
+ // $upload is a multi-dimensional array:
1351
+ foreach ($upload['tmp_name'] as $index => $value) {
1352
+ $files[] = $this->handle_file_upload(
1353
+ $upload['tmp_name'][$index],
1354
+ $file_name ? $file_name : $upload['name'][$index],
1355
+ $size ? $size : $upload['size'][$index],
1356
+ $upload['type'][$index],
1357
+ $upload['error'][$index],
1358
+ $index,
1359
+ $content_range
1360
+ );
1361
+ }
1362
+ } else {
1363
+ // param_name is a single object identifier like "file",
1364
+ // $upload is a one-dimensional array:
1365
+ $files[] = $this->handle_file_upload(
1366
+ isset($upload['tmp_name']) ? $upload['tmp_name'] : null,
1367
+ $file_name ? $file_name : (isset($upload['name']) ?
1368
+ $upload['name'] : null),
1369
+ $size ? $size : (isset($upload['size']) ?
1370
+ $upload['size'] : $this->get_server_var('CONTENT_LENGTH')),
1371
+ isset($upload['type']) ?
1372
+ $upload['type'] : $this->get_server_var('CONTENT_TYPE'),
1373
+ isset($upload['error']) ? $upload['error'] : null,
1374
+ null,
1375
+ $content_range
1376
+ );
1377
+ }
1378
+ }
1379
+ $response = array($this->options['param_name'] => $files);
1380
+ return $this->generate_response($response, $print_response);
1381
+ }
1382
+
1383
+ public function delete($print_response = true) {
1384
+ $file_names = $this->get_file_names_params();
1385
+ if (empty($file_names)) {
1386
+ $file_names = array($this->get_file_name_param());
1387
+ }
1388
+ $response = array();
1389
+ foreach ($file_names as $file_name) {
1390
+ $file_path = $this->get_upload_path($file_name);
1391
+ $success = is_file($file_path) && $file_name[0] !== '.' && unlink($file_path);
1392
+ if ($success) {
1393
+ foreach ($this->options['image_versions'] as $version => $options) {
1394
+ if (!empty($version)) {
1395
+ $file = $this->get_upload_path($file_name, $version);
1396
+ if (is_file($file)) {
1397
+ unlink($file);
1398
+ }
1399
+ }
1400
+ }
1401
+ }
1402
+ $response[$file_name] = $success;
1403
+ }
1404
+ return $this->generate_response($response, $print_response);
1405
+ }
1406
+
1407
+ protected function basename($filepath, $suffix = null) {
1408
+ $splited = preg_split('/\//', rtrim ($filepath, '/ '));
1409
+ return substr(basename('X'.$splited[count($splited)-1], $suffix), 1);
1410
+ }
1411
+ }