PluginProbe
Import WP – CSV & XML Import Export for WordPress / 2.15.1
Import WP – CSV & XML Import Export for WordPress v2.15.1
2.15.1 2.15.0 2.14.24 2.14.23 2.7.0 2.7.1 2.7.10 2.7.11 2.7.12 2.7.13 2.7.14 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.8.0 2.8.1 2.8.2 2.8.3 2.9.0 2.9.1 All 144 releases
jc-importer / class / Common / Filesystem / Filesystem.php

Filesystem.php in Import WP – CSV & XML Import Export for WordPress 2.15.1, at class/Common/Filesystem/Filesystem.php

438 lines 13.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace ImportWP\Common\Filesystem;
4
5 use ImportWP\Common\Http\Http;
6 use ImportWP\Common\Util\Logger;
7 use ImportWP\Common\Util\Singleton;
8 use ImportWP\Container;
9 use ImportWP\EventHandler;
10
11 class Filesystem
12 {
13 use Singleton;
14
15 /**
16 * @var ImportWP\Container\Container
17 */
18 private $container;
19
20 /**
21 * @var EventHandler $event_handler
22 */
23 private $event_handler;
24
25 public function __construct(EventHandler $event_handler)
26 {
27 $this->event_handler = $event_handler;
28 $this->container = Container::getInstance();
29 }
30
31 public function copy($source, $destination, $allowed_mimes = null, $filetype = null)
32 {
33
34 if (!file_exists($source)) {
35 Logger::write('File doesn`t exist on local filesystem: ' . $source);
36 return new \WP_Error('IWP_FS_7', __('File doesn`t exist on local filesystem.', 'jc-importer'));
37 }
38
39 if (is_null($filetype)) {
40 $filetype = $this->get_filetype($source);
41 }
42
43 if (!is_null($allowed_mimes)) {
44
45 if (!$filetype) {
46 Logger::write('Unable to determine filetype: ' . $source);
47 return new \WP_Error('IWP_FS_6', __('Unable to determine filetype.', 'jc-importer'));
48 }
49
50 if (!in_array($filetype, $allowed_mimes, true)) {
51 Logger::write('Invalid filetype: ' . $filetype . ', allowed(' . implode(', ', $allowed_mimes) . ')');
52 return new \WP_Error('IWP_FS_4', __('Invalid filetype.', 'jc-importer'));
53 }
54 }
55
56 if (!copy($source, $destination)) {
57 return new \WP_Error('IWP_FS_4', sprintf(__('Unable to copy file: %s.', 'jc-importer'), $source));
58 }
59
60 $type = $this->get_file_mime($source);
61 Logger::write('Copied file: ' . $destination . ' -type=' . $filetype . ' -mime=' . $type);
62 return true;
63 }
64
65 public function upload_file($attachment, $allowed_mimes = null)
66 {
67 // use built in wordpress file upload
68 if (!function_exists('wp_handle_upload')) {
69 require_once ABSPATH . "wp-admin" . '/includes/file.php';
70 }
71
72 $uploaded_file = wp_handle_upload($attachment, ['test_form' => false, 'test_type' => false]);
73
74 if (isset($uploaded_file['error'])) {
75 Logger::write($uploaded_file['error']);
76 return new \WP_Error('IWP_FS_UF', $uploaded_file['error']);
77 }
78
79 $file = $uploaded_file['file'];
80 $type = $uploaded_file['type'];
81
82 $filetype = $this->check_mime_header($uploaded_file['type']);
83
84 // if header doesnt match check for file extension.
85 if (!$filetype) {
86 $filetype = $this->get_filetype_from_ext($attachment['name']);
87 }
88
89 // determine file type from mimetype.
90 if (!is_null($allowed_mimes) && !in_array($filetype, $allowed_mimes, true)) {
91 Logger::write('Invalid filetype: ' . $filetype . ', allowed(' . implode(', ', $allowed_mimes) . ')');
92 return new \WP_Error('IWP_FS_4', __('Invalid filetype.', 'jc-importer'));
93 }
94
95 Logger::write('Uploaded file: ' . $file . ' -type=' . $filetype . ' -mime=' . $type);
96
97 return array(
98 'dest' => $file,
99 'type' => $filetype,
100 'mime' => $type
101 );
102 }
103
104 public function download_file($remote_url, $filetype = null, $allowed_mimes = null, $override_filename = null, $prefix = '')
105 {
106 $remote_url_temp = strtok($remote_url, '?');
107
108 if (!is_null($allowed_mimes)) {
109 if (is_null($filetype)) {
110 $filetype = $this->get_filetype_from_ext($remote_url_temp);
111 }
112 if (!in_array($filetype, $allowed_mimes, true)) {
113 Logger::write('Invalid filetype: ' . $filetype . ', allowed(' . implode(', ', $allowed_mimes) . ')');
114 return new \WP_Error('IWP_FS_4', __('Invalid filetype.', 'jc-importer'));
115 }
116 }
117
118 $wp_upload_dir = wp_upload_dir();
119 $filename = !empty($override_filename) ? $override_filename : $prefix . basename($remote_url_temp);
120
121 // force extension of file if it doesnt match
122 if ($filetype === 'xml') {
123 if (preg_match('/\.(xml|zip|gz)$/', $filename) === 0) {
124 $filename .= '.xml';
125 }
126 } elseif ($filetype === 'csv') {
127 if (preg_match('/\.(csv|zip|gz)$/', $filename) === 0) {
128 $filename .= '.csv';
129 }
130 } elseif ($filetype === 'json') {
131 if (preg_match('/\.(json|zip|gz)$/', $filename) === 0) {
132 $filename .= '.json';
133 }
134 }
135
136 $dest = wp_unique_filename($wp_upload_dir['path'], $filename);
137 $wp_dest = $wp_upload_dir['path'] . '/' . $dest;
138 touch($wp_dest);
139
140 /**
141 * @var Http $http
142 */
143 $http = Container::getInstance()->get('http');
144
145 $headers = [];
146 if ($filetype === 'xml') {
147 $headers['Content-Type'] = 'text/xml';
148 $headers['Accept'] = 'text/xml';
149 } elseif ($filetype === 'csv') {
150 $headers['Content-Type'] = 'text/csv';
151 $headers['Accept'] = 'text/csv';
152 } elseif ($filetype === 'json') {
153 $headers['Content-Type'] = 'application/json';
154 $headers['Accept'] = 'application/json';
155 }
156
157 $result = $http->download_file_stream($remote_url, $wp_dest, $headers);
158 if (is_wp_error($result)) {
159 @unlink($wp_dest);
160 Logger::write($result->get_error_message());
161 return $result;
162 }
163
164 if (is_string($result)) {
165 $filename = !empty($override_filename) ? $override_filename : $prefix . basename($result);
166 $dest = wp_unique_filename($wp_upload_dir['path'], $filename);
167 $wp_tmp_dest = $wp_upload_dir['path'] . '/' . $dest;
168
169 if (copy($wp_dest, $wp_tmp_dest)) {
170 Logger::write('Rename file: ' . $wp_dest . ' -output=' . $wp_tmp_dest);
171 unlink($wp_dest);
172 $wp_dest = $wp_tmp_dest;
173 }
174 }
175
176 $exists = $this->file_exists($wp_dest);
177 if (is_wp_error($exists)) {
178 return $exists;
179 }
180
181 $type = $this->get_file_mime($wp_dest);
182 Logger::write('Downloaded file: ' . $wp_dest . ' -type=' . $filetype . ' -mime=' . $type);
183
184 return array(
185 'dest' => $wp_dest,
186 'type' => $filetype,
187 'mime' => $type
188 );
189 }
190
191 public function file_exists($src)
192 {
193 try {
194 if (!is_string($src) || $src === '' || !file_exists($src)) {
195 throw new \Exception(sprintf(__("File not found: %s", 'jc-importer'), is_string($src) ? $src : ''));
196 }
197
198 $size = filesize($src);
199 if ($size == 0) {
200 unlink($src);
201 throw new \Exception(sprintf(__("File not found or empty: %s", 'jc-importer'), $src));
202 }
203 } catch (\Exception $e) {
204 return new \WP_Error('IWP_FS_8', $e->getMessage());
205 }
206
207 return true;
208 }
209
210 public function copy_file($remote_url, $allowed_mimes = null, $override_filename = null, $prefix = '', $filetype = null)
211 {
212 $remote_url = strtok($remote_url, '?');
213
214 $wp_upload_dir = wp_upload_dir();
215
216 $filename = !empty($override_filename) ? $override_filename : $prefix . basename($remote_url);
217 $dest = wp_unique_filename($wp_upload_dir['path'], $filename);
218 $wp_dest = $wp_upload_dir['path'] . '/' . $dest;
219
220 $result = $this->copy($remote_url, $wp_dest, $allowed_mimes, $filetype);
221 if (is_wp_error($result)) {
222 return $result;
223 }
224
225 return array(
226 'dest' => $wp_dest,
227 'type' => is_null($filetype) ? $this->get_filetype($wp_dest) : $filetype,
228 'mime' => $this->get_file_mime($wp_dest)
229 );
230 }
231
232 public function string_to_file($string, $filename)
233 {
234 $wp_upload_dir = wp_upload_dir();
235 $dest = wp_unique_filename($wp_upload_dir['path'], $filename);
236 $wp_dest = $wp_upload_dir['path'] . '/' . $dest;
237
238 if (file_put_contents($wp_dest, $string) === false) {
239 return new \WP_Error('IWP_FS_SF', __("Unable to write string to file", 'jc-importer'));
240 }
241
242 return array(
243 'dest' => $wp_dest,
244 'type' => $this->get_filetype($wp_dest),
245 'mime' => $this->get_file_mime($wp_dest)
246 );
247 }
248
249
250
251 /**
252 * Get and/or create the plugins tmp directory
253 *
254 * @return string
255 */
256
257 public function get_temp_directory($url = false, $folder = 'importwp')
258 {
259 $dir = wp_upload_dir();
260
261 $base = $url ? $dir['baseurl'] : $dir['basedir'];
262 $ds = $url ? '/' : DIRECTORY_SEPARATOR;
263 $path = $base . $ds . $folder;
264
265 // create folders and files if required, force to be dir path instead of url.
266 $dir_path = $dir['basedir'] . DIRECTORY_SEPARATOR . $folder;
267 if (!is_dir($dir_path)) {
268 mkdir($dir_path);
269 }
270
271 if (!file_exists($dir_path . '/.htaccess')) {
272 file_put_contents($dir_path . '/.htaccess', "# Apache 2.4+
273 <IfModule mod_authz_core.c>
274 Require all denied
275 </IfModule>
276
277 # Apache 2.2 and older (or when mod_authz_core isn't available)
278 <IfModule !mod_authz_core.c>
279 Deny from all
280 </IfModule>");
281 }
282
283 if (!file_exists($dir_path . '/index.html')) {
284 touch($dir_path . '/index.html');
285 }
286
287 return $path;
288 }
289
290 /**
291 * Whether a path looks absolute (Unix or Windows).
292 *
293 * @param string $path
294 * @return bool
295 */
296 public static function is_absolute_path($path)
297 {
298 if (!is_string($path) || $path === '') {
299 return false;
300 }
301
302 if ($path[0] === '/' || $path[0] === '\\') {
303 return true;
304 }
305
306 return strlen($path) > 2 && ctype_alpha($path[0]) && $path[1] === ':';
307 }
308
309 /**
310 * Convert an absolute path under the uploads basedir to a relative path.
311 * Paths outside uploads are returned unchanged.
312 *
313 * @param string $path
314 * @return string
315 */
316 public static function to_uploads_relative_path($path)
317 {
318 $path = wp_normalize_path((string) $path);
319 $basedir = wp_normalize_path(untrailingslashit(wp_upload_dir()['basedir']));
320
321 if ($path === $basedir) {
322 return '';
323 }
324
325 if (strpos($path, $basedir . '/') === 0) {
326 return ltrim(substr($path, strlen($basedir)), '/');
327 }
328
329 return $path;
330 }
331
332 /**
333 * Resolve a stored importer file path to an absolute path under the current uploads directory.
334 *
335 * Avoids calling file_exists() on legacy absolute paths that may sit outside open_basedir
336 * after a site move / hosting path change.
337 *
338 * @param string $stored
339 * @return string|false Absolute filesystem path if the file exists, otherwise false.
340 */
341 public static function resolve_importer_file_path($stored)
342 {
343 if (!is_string($stored) || $stored === '') {
344 return false;
345 }
346
347 $stored = wp_normalize_path(trim($stored));
348 $basedir = wp_normalize_path(untrailingslashit(wp_upload_dir()['basedir']));
349 $candidates = [];
350
351 if (self::is_absolute_path($stored)) {
352 if (strpos($stored, $basedir . '/') === 0 || $stored === $basedir) {
353 $candidates[] = $stored;
354 } else {
355 // Remap legacy absolute paths that still contain the importwp suffix.
356 if (preg_match('#/(importwp(?:/[^/]+)*/.+)$#', $stored, $matches)) {
357 $candidates[] = $basedir . '/' . ltrim($matches[1], '/');
358 }
359 $candidates[] = $basedir . '/importwp/uploads/' . basename($stored);
360 }
361 } else {
362 $candidates[] = $basedir . '/' . ltrim($stored, '/');
363 }
364
365 foreach (array_unique($candidates) as $candidate) {
366 $candidate = wp_normalize_path($candidate);
367
368 // Never probe paths outside the current uploads basedir (open_basedir safe).
369 if ($candidate !== $basedir && strpos($candidate, $basedir . '/') !== 0) {
370 continue;
371 }
372
373 if (file_exists($candidate)) {
374 return $candidate;
375 }
376 }
377
378 return false;
379 }
380
381 public function check_mime_header($mime)
382 {
383 switch ($mime) {
384 case 'text/comma-separated-values':
385 case 'text/csv':
386 case 'application/csv':
387 case 'application/excel':
388 case 'application/vnd.ms-excel':
389 case 'application/vnd.msexcel':
390 case 'text/anytext':
391 case 'text/plain':
392 return 'csv';
393 case 'text/xml':
394 case 'application/xml':
395 case 'application/x-xml':
396 return 'xml';
397 case 'application/json':
398 case 'text/json':
399 return 'json';
400 }
401
402 return $this->event_handler->run('importer.allowed_mime_types', [false, $mime]);
403 }
404
405 public function get_filetype($file)
406 {
407 $mime_type = $this->get_file_mime($file);
408 if ($mime_type) {
409 return $this->check_mime_header($mime_type);
410 }
411
412 return $this->get_filetype_from_ext($file);
413 }
414
415 public function get_file_mime($file)
416 {
417 $check = wp_check_filetype_and_ext($file, basename($file));
418 return $check['type'];
419 }
420
421 public function get_filetype_from_ext($file)
422 {
423
424 $filetype = null;
425 if (stripos($file, '.csv')) {
426 $filetype = 'csv';
427 } elseif (stripos($file, '.xml')) {
428 $filetype = 'xml';
429 } elseif (stripos($file, '.json')) {
430 $filetype = 'json';
431 }
432
433 $filetype = apply_filters('iwp/get_filetype_from_ext', $filetype, $file);
434
435 return $filetype;
436 }
437 }
438