PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.0.4
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.0.4
11.1.1 11.1.0 11.0.5 11.0.4 11.0.3 11.0.2 11.0.1 11.0.0 10.11.1 10.11.0 10.10.2 10.10.1 10.10.0 10.9.1 trunk 10.0.0 10.0.1 10.1.0 10.1.1 10.1.2 10.1.3 10.1.4 10.2.0 10.2.1 10.2.2 All 62 releases
acymailing / back / Helpers / global / file.php

file.php in AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress 11.0.4, at back/Helpers/global/file.php

702 lines 21.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 defined('ABSPATH') || die('Restricted Access');
3
4 use AcyMailing\Helpers\ImageHelper;
5
6 /**
7 * Function to return the number of bytes of a val like 2M
8 */
9 function acym_bytes(string $val): int
10 {
11 $val = trim($val);
12 if (empty($val)) {
13 return 0;
14 }
15 $last = strtolower($val[strlen($val) - 1]);
16 switch ($last) {
17 case 'g':
18 $val = intval($val) * 1073741824;
19 break;
20 case 'm':
21 $val = intval($val) * 1048576;
22 break;
23 case 'k':
24 $val = intval($val) * 1024;
25 break;
26 }
27
28 return (int)$val;
29 }
30
31 //Create a dir and an index.html file inside
32 function acym_createDir(string $dir, bool $report = true, bool $secured = false): bool
33 {
34 if (is_dir($dir)) {
35 return true;
36 }
37
38 $indexhtml = '<html><body bgcolor="#FFFFFF"></body></html>';
39
40 //Create the directory with an index file inside
41 try {
42 $status = acym_createFolder($dir);
43 } catch (Exception $e) {
44 $status = false;
45 }
46
47 if (!$status) {
48 if ($report) {
49 acym_display('Could not create the directory '.$dir, 'error');
50 }
51
52 return false;
53 }
54
55 try {
56 $status = acym_writeFile($dir.DS.'index.html', $indexhtml);
57 } catch (Exception $e) {
58 $status = false;
59 }
60
61 if (!$status && $report) {
62 acym_display('Could not create the file '.$dir.DS.'index.html', 'error');
63 }
64
65 if ($secured) {
66 try {
67 $htaccess = 'Order deny,allow'."\r\n".'Deny from all';
68 $status = acym_writeFile($dir.DS.'.htaccess', $htaccess);
69 } catch (Exception $e) {
70 $status = false;
71 }
72
73 if (!$status && $report) {
74 acym_display('Could not create the file '.$dir.DS.'.htaccess', 'error');
75 }
76 }
77
78 return $status;
79 }
80
81 function acym_importFile(array $file, string $uploadPath, bool $onlyPict): ?string
82 {
83 // Check the token... no import without token!
84 wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
85
86 $config = acym_config();
87 $additionalMsg = '';
88
89 if ($file['error'] > 0) {
90 $file['error'] = intval($file['error']);
91 if ($file['error'] > 8) {
92 $file['error'] = 0;
93 }
94
95 $phpFileUploadErrors = [
96 0 => 'Unknown error',
97 1 => 'The uploaded file exceeds the upload_max_filesize directive in php.ini',
98 2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
99 3 => 'The uploaded file was only partially uploaded',
100 4 => 'No file was uploaded',
101 6 => 'Missing a temporary folder',
102 7 => 'Failed to write file to disk',
103 8 => 'A PHP extension stopped the file upload',
104 ];
105
106 acym_enqueueMessage(acym_translationSprintf('ACYM_ERROR_UPLOADING_FILE_X', $phpFileUploadErrors[$file['error']]), 'error');
107
108 return null;
109 }
110
111 acym_createDir($uploadPath, true);
112
113 if (!acym_isWritable($uploadPath)) {
114 acym_chmod($uploadPath, 0755);
115 if (!acym_isWritable($uploadPath)) {
116 acym_display(acym_translationSprintf('ACYM_WRITABLE_FOLDER', $uploadPath), 'error');
117
118 return null;
119 }
120 }
121
122 if ($onlyPict) {
123 $allowedExtensions = ['png', 'jpeg', 'jpg', 'gif', 'ico', 'bmp'];
124 } else {
125 $allowedExtensions = explode(',', $config->get('allowed_files'));
126 }
127
128 // We don't allow to upload anything else than a picture extension
129 if (!preg_match('#\.('.implode('|', $allowedExtensions).')$#Ui', $file['name'], $extension)) {
130 $ext = substr($file['name'], strrpos($file['name'], '.') + 1);
131 acym_display(
132 acym_translationSprintf(
133 'ACYM_ACCEPTED_TYPE',
134 esc_html($ext),
135 implode(', ', $allowedExtensions)
136 ),
137 'error'
138 );
139
140 return null;
141 }
142
143 // We will never allow some files to be uploaded...
144 // This should never happen... only if there is an hack tentative so no need to translate this error message
145 if (preg_match('#\.(php.?|.?htm.?|pl|py|jsp|asp|sh|cgi)#Ui', $file['name'])) {
146 acym_display(
147 'This extension name is blocked by the system regardless your configuration for security reasons',
148 'error'
149 );
150
151 return null;
152 }
153
154 // We remove all dots or space from the file name to avoid the double extension security issue and the fact some mail clients don't like spaces
155 $file['name'] = str_replace(
156 ['-', '.', ' ', '\''],
157 '_',
158 substr($file['name'], 0, strrpos($file['name'], '.'))
159 ).'.'.$extension[1];
160
161 if ($onlyPict) {
162 // Extra security: we make sure we only have a valid picture format
163 $imageSize = @getimagesize($file['tmp_name']);
164 if (empty($imageSize)) {
165 acym_display(acym_translation('ACYM_INVALID_IMAGE'), 'error');
166
167 return null;
168 }
169 }
170
171 if (file_exists($uploadPath.DS.$file['name'])) {
172 $i = 1;
173 $nameFile = preg_replace('/\\.[^.\\s]{3,4}$/', '', $file['name']);
174 $ext = substr($file['name'], strrpos($file['name'], '.') + 1);
175 while (file_exists($uploadPath.DS.$nameFile.'_'.$i.'.'.$ext)) {
176 $i++;
177 }
178
179 $file['name'] = $nameFile.'_'.$i.'.'.$ext;
180 $additionalMsg = '<br />'.acym_translationSprintf('ACYM_FILE_RENAMED', $file['name']);
181 if ($onlyPict) {
182 $additionalMsg .= '<br /><a style="color: blue; cursor: pointer;" onclick="confirmBox(\'rename\', \''.$file['name'].'\', \''.$nameFile.'.'.$ext.'\')">'.acym_translation(
183 'ACYM_RENAME_OR_REPLACE'
184 ).'</a>';
185 }
186 }
187
188 if (!acym_uploadFile($file['tmp_name'], rtrim($uploadPath, DS).DS.$file['name'])) {
189 acym_display(
190 acym_translationSprintf(
191 'ACYM_FAIL_UPLOAD',
192 '<b><i>'.esc_html($file['tmp_name']).'</i></b>',
193 '<b><i>'.esc_html(rtrim($uploadPath, DS).DS.$file['name']).'</i></b>'
194 ),
195 'error'
196 );
197
198 return null;
199 }
200
201 if ($onlyPict && $imageSize[0] > 1000) {
202 $imageHelper = new ImageHelper();
203 if ($imageHelper->available()) {
204 $imageHelper->maxHeight = 9999;
205 $imageHelper->maxWidth = 700;
206 $message = 'ACYM_IMAGE_RESIZED';
207 $imageHelper->destination = $uploadPath;
208 $thumb = $imageHelper->generateThumbnail(rtrim($uploadPath, DS).DS.$file['name']);
209 if (!empty($thumb)) {
210 acym_moveFile($thumb['file'], $uploadPath.DS.$file['name']);
211 $additionalMsg .= '<br />'.acym_translation($message);
212 }
213 }
214 }
215 acym_enqueueMessage(acym_translation('ACYM_SUCCESS_FILE_UPLOAD').$additionalMsg, 'success');
216
217 return $file['name'];
218 }
219
220 function acym_inputFile(
221 string $name,
222 string $value = '',
223 string $class = '',
224 array $attributes = [],
225 string $downloadUrl = ''
226 ): void {
227 $hasValue = !empty($value);
228 $noFileText = acym_translation('ACYM_NO_FILE_CHOSEN');
229 $baseName = substr($name, -2) === '[]' ? substr($name, 0, -2) : $name;
230 ?>
231
232 <div class="cell <?php echo esc_attr($class); ?> grid-x acym__input__file__container">
233 <input type="hidden" name="<?php echo esc_attr($baseName); ?>" value="<?php echo esc_attr($value); ?>" class="acym__input__file__clear">
234 <input <?php
235 foreach ($attributes as $oneAttribute => $oneValue) {
236 if (is_array($oneValue) || is_object($oneValue)) {
237 $oneValue = json_encode($oneValue);
238 } elseif ($oneValue === true) {
239 $oneValue = $oneAttribute;
240 }
241
242 echo ' '.esc_attr($oneAttribute).'="'.esc_attr($oneValue).'"';
243 }
244 ?> style="display: none" type="file" name="<?php echo esc_attr($name); ?>">
245 <button type="button" class="acym__button__file button button-secondary cell shrink">
246 <?php echo esc_html(acym_translation('ACYM_CHOOSE_FILE')); ?>
247 </button>
248 <span class="cell shrink margin-left-2 margin-right-2 acym__input__file__name" data-no-file="<?php echo esc_attr($noFileText); ?>">
249 <?php echo esc_html($hasValue ? $value : $noFileText); ?>
250 </span>
251 <i class="acym__input__file__download cell shrink acym__color__blue cursor-pointer acymicon-download margin-right-1"
252 <?php echo $hasValue ? '' : ' style="display: none;"'; ?>
253 <?php echo !empty($downloadUrl) ? ' data-download-url="'.esc_url($downloadUrl).'"' : ''; ?>
254 title="<?php echo esc_attr(acym_translation('ACYM_DOWNLOAD')); ?>" aria-label="<?php echo esc_attr(acym_translation('ACYM_DOWNLOAD')); ?>"></i>
255 <i class="acymicon-close acym__color__red acym__input__file__delete cursor-pointer cell shrink margin-left-1"
256 <?php echo $hasValue ? '' : ' style="display: none;"'; ?>
257 title="<?php echo esc_attr(acym_translation('ACYM_DELETE')); ?>" aria-label="<?php echo esc_attr(acym_translation('ACYM_DELETE')); ?>"></i>
258 </div>
259 <?php
260 }
261
262 function acym_getFilesFolder(bool $ignoreVariables = false): string
263 {
264 $config = acym_config();
265 $uploadFolder = $config->get('uploadfolder', ACYM_UPLOAD_FOLDER);
266 if ($ignoreVariables) {
267 $uploadFolder = str_replace(['{userid}', '{groupname}', '{year}', '{month}', '{day}'], '', $uploadFolder);
268 }
269 $uploadFolder = trim($uploadFolder, '/');
270
271 $uploadFolder = str_replace('{userid}', acym_currentUserId(), $uploadFolder);
272 $uploadFolder = str_replace('{year}', gmdate('Y'), $uploadFolder);
273 $uploadFolder = str_replace('{month}', gmdate('m'), $uploadFolder);
274 $uploadFolder = str_replace('{day}', gmdate('d'), $uploadFolder);
275
276 return acym_replaceGroupTags($uploadFolder);
277 }
278
279 /**
280 * Find all sub folders...
281 * Return an array of all folders ordered properly
282 */
283 function acym_generateArborescence(array $folders): array
284 {
285 //Recursive algorythm to catch subfolders
286 $folderList = [];
287 foreach ($folders as $folder) {
288 $folderPath = acym_cleanPath(ACYM_ROOT.trim(str_replace('/', DS, trim($folder)), DS));
289 if (!file_exists($folderPath)) {
290 acym_createDir($folderPath);
291 }
292 $subFolders = acym_listFolderTree($folderPath, '', 15);
293 $folderList[$folder] = [];
294 foreach ($subFolders as $oneFolder) {
295 $subFolder = str_replace(ACYM_ROOT, '', $oneFolder['relname']);
296 $subFolder = str_replace(DS, '/', $subFolder);
297 $folderList[$folder][$subFolder] = ltrim($subFolder, '/');
298 }
299 $folderList[$folder] = array_unique($folderList[$folder]);
300 }
301
302 return $folderList;
303 }
304
305 function acym_makeSafeFile(string $file): string
306 {
307 $file = rtrim($file, '.');
308 $regex = ['#(\.){2,}#', '#[^A-Za-z0-9\.\_\- ]#', '#^\.#'];
309
310 return trim(preg_replace($regex, '', $file));
311 }
312
313 function acym_getFolders(
314 string $path,
315 string $filter = '.',
316 bool $recurse = false,
317 bool $full = false,
318 array $exclude = ['.svn', 'CVS', '.DS_Store', '__MACOSX'],
319 array $excludefilter = ['^\..*']
320 ): array {
321 $path = acym_cleanPath($path);
322
323 if (!is_dir($path)) {
324 acym_enqueueMessage(acym_translationSprintf('ACYM_IS_NOT_A_FOLDER', $path), 'error');
325
326 return [];
327 }
328
329 if (count($excludefilter)) {
330 $excludefilter_string = '/('.implode('|', $excludefilter).')/';
331 } else {
332 $excludefilter_string = '';
333 }
334
335 $arr = acym_getItems($path, $filter, $recurse, $full, $exclude, $excludefilter_string, false);
336 asort($arr);
337
338 return array_values($arr);
339 }
340
341 function acym_getFiles(
342 string $path,
343 string $filter = '.',
344 bool $recurse = false,
345 bool $full = false,
346 array $exclude = ['.svn', 'CVS', '.DS_Store', '__MACOSX'],
347 array $excludefilter = [
348 '^\..*',
349 '.*~',
350 ],
351 bool $naturalSort = false
352 ): array {
353 $path = acym_cleanPath($path);
354
355 if (!is_dir($path)) {
356 acym_enqueueMessage(acym_translationSprintf('ACYM_IS_NOT_A_FOLDER', $path), 'error');
357
358 return [];
359 }
360
361 if (count($excludefilter)) {
362 $excludefilter_string = '/('.implode('|', $excludefilter).')/';
363 } else {
364 $excludefilter_string = '';
365 }
366
367 $arr = acym_getItems($path, $filter, $recurse, $full, $exclude, $excludefilter_string, true);
368
369 if ($naturalSort) {
370 natsort($arr);
371 } else {
372 asort($arr);
373 }
374
375 return array_values($arr);
376 }
377
378 function acym_getItems(
379 string $path,
380 string $filter,
381 bool $recurse,
382 bool $full,
383 array $exclude,
384 string $excludefilter_string,
385 bool $findfiles
386 ): array {
387 $arr = [];
388
389 if (!($handle = @opendir($path))) {
390 return $arr;
391 }
392
393 while (($file = readdir($handle)) !== false) {
394 if (
395 $file == '.'
396 || $file == '..'
397 || in_array($file, $exclude)
398 || (!empty($excludefilter_string) && preg_match($excludefilter_string, $file))
399 ) {
400 continue;
401 }
402 $fullpath = rtrim($path, '/').'/'.$file;
403
404 $isDir = is_dir($fullpath);
405
406 if (($isDir xor $findfiles) && preg_match("/$filter/", $file)) {
407 if ($full) {
408 $arr[] = $fullpath;
409 } else {
410 $arr[] = $file;
411 }
412 }
413
414 if ($isDir && $recurse) {
415 $arr = array_merge(
416 $arr,
417 acym_getItems(
418 $fullpath,
419 $filter,
420 true,
421 $full,
422 $exclude,
423 $excludefilter_string,
424 $findfiles
425 )
426 );
427 }
428 }
429
430 closedir($handle);
431
432 return $arr;
433 }
434
435 function acym_copyFolder(string $src, string $dest, ?string $path = null, bool $force = false, bool $use_streams = false): bool
436 {
437 if (!empty($path)) {
438 $src = acym_cleanPath($path.'/'.$src);
439 $dest = acym_cleanPath($path.'/'.$dest);
440 }
441
442 $src = rtrim($src, DIRECTORY_SEPARATOR);
443 $dest = rtrim($dest, DIRECTORY_SEPARATOR);
444
445 if (!file_exists($src)) {
446 acym_enqueueMessage(acym_translationSprintf('ACYM_FOLDER_DOES_NOT_EXIST', $src), 'error');
447
448 return false;
449 }
450
451 if (file_exists($dest) && !$force) {
452 acym_enqueueMessage(acym_translationSprintf('ACYM_FOLDER_ALREADY_EXIST', $dest), 'error');
453
454 return true;
455 }
456
457 if (!acym_createFolder($dest)) {
458 acym_enqueueMessage(acym_translation('ACYM_CANNOT_CREATE_DESTINATION_FOLDER'), 'error');
459
460 return false;
461 }
462
463 if (!($dh = @opendir($src))) {
464 acym_enqueueMessage(acym_translation('ACYM_CANNOT_OPEN_SOURCE_FOLDER'), 'error');
465
466 return false;
467 }
468
469 while (($file = readdir($dh)) !== false) {
470 $sfid = $src.'/'.$file;
471 $dfid = $dest.'/'.$file;
472
473 switch (filetype($sfid)) {
474 case 'dir':
475 if ($file != '.' && $file != '..') {
476 $ret = acym_copyFolder($sfid, $dfid, null, $force, $use_streams);
477
478 if ($ret !== true) {
479 return $ret;
480 }
481 }
482 break;
483
484 case 'file':
485 if (!@copy($sfid, $dfid)) {
486 acym_enqueueMessage(acym_translationSprintf('ACYM_COPY_FILE_FAILED_PERMISSION', $sfid), 'error');
487
488 return false;
489 }
490 break;
491 }
492 }
493
494 return true;
495 }
496
497 function acym_listFolderTree(string $path, string $filter, int $maxLevel = 3, int $level = 0, int $parent = 0): array
498 {
499 $dirs = [];
500
501 if ($level === 0) {
502 $GLOBALS['acym_folder_tree_index'] = 0;
503 }
504
505 if ($level < $maxLevel) {
506 $folders = acym_getFolders($path, $filter);
507
508 foreach ($folders as $name) {
509 $id = ++$GLOBALS['acym_folder_tree_index'];
510 $fullName = acym_cleanPath($path.'/'.$name);
511 $dirs[] = [
512 'id' => $id,
513 'parent' => $parent,
514 'name' => $name,
515 'fullname' => $fullName,
516 'relname' => str_replace(ACYM_ROOT, '', $fullName),
517 ];
518 $dirs2 = acym_listFolderTree($fullName, $filter, $maxLevel, $level + 1, $id);
519 $dirs = array_merge($dirs, $dirs2);
520 }
521 }
522
523 return $dirs;
524 }
525
526 /**
527 * @param mixed $buffer
528 */
529 function acym_writeFile(string $file, $buffer, int $flags = 0): bool
530 {
531 if (!file_exists(dirname($file)) && !acym_createFolder(dirname($file))) {
532 return false;
533 }
534
535 $file = acym_cleanPath($file);
536
537 return is_int(file_put_contents($file, $buffer, $flags));
538 }
539
540 function acym_copyFile(string $src, string $dest, string $path = ''): bool
541 {
542 if (!empty($path)) {
543 $src = acym_cleanPath($path.DS.$src);
544 $dest = acym_cleanPath($path.DS.$dest);
545 }
546
547 if (!is_readable($src)) {
548 acym_enqueueMessage(acym_translationSprintf('ACYM_COULD_NOT_FIND_FILE_SOURCE_PERMISSION', $src), 'error');
549
550 return false;
551 }
552
553 if (!@copy($src, $dest)) {
554 acym_enqueueMessage(acym_translationSprintf('ACYM_COULD_NOT_COPY_FILE_X_TO_X', $src, $dest), 'error');
555
556 return false;
557 }
558
559 return true;
560 }
561
562 function acym_fileGetExt(string $file): string
563 {
564 $pathInfo = pathinfo($file);
565
566 return empty($pathInfo['extension']) ? '' : strtolower($pathInfo['extension']);
567 }
568
569 function acym_cleanPath(string $path, string $ds = DIRECTORY_SEPARATOR): string
570 {
571 $path = trim($path);
572
573 if (empty($path)) {
574 $path = ACYM_ROOT;
575 } elseif (($ds === '\\') && substr($path, 0, 2) == '\\\\') {
576 $path = "\\".preg_replace('#[/\\\\]+#', $ds, $path);
577 } else {
578 $path = preg_replace('#[/\\\\]+#', $ds, $path);
579 }
580
581 return $path;
582 }
583
584 function acym_createArchive(string $name, array $files): bool
585 {
586 $contents = [];
587 $ctrldir = [];
588
589 $timearray = getdate();
590 $dostime = (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) | ($timearray['hours'] << 11) | ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1);
591 $dtime = dechex($dostime);
592 $hexdtime = chr(hexdec($dtime[6].$dtime[7])).chr(hexdec($dtime[4].$dtime[5])).chr(hexdec($dtime[2].$dtime[3])).chr(
593 hexdec($dtime[0].$dtime[1])
594 );
595
596 foreach ($files as $file) {
597 $data = $file['data'];
598 $filename = str_replace('\\', '/', $file['name']);
599
600 $fr = "\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00".$hexdtime;
601
602 $unc_len = strlen($data);
603 $crc = crc32($data);
604 $zdata = gzcompress($data);
605 $zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2);
606 $c_len = strlen($zdata);
607
608 $fr .= pack('V', $crc).pack('V', $c_len).pack('V', $unc_len).pack('v', strlen($filename)).pack(
609 'v',
610 0
611 ).$filename.$zdata;
612
613 $old_offset = strlen(implode('', $contents));
614 $contents[] = $fr;
615
616 $cdrec = "\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00".$hexdtime;
617 $cdrec .= pack('V', $crc).pack('V', $c_len).pack('V', $unc_len).pack('v', strlen($filename)).pack('v', 0).pack(
618 'v',
619 0
620 ).pack('v', 0).pack('v', 0).pack('V', 32).pack('V', $old_offset).$filename;
621
622 $ctrldir[] = $cdrec;
623 }
624
625 $data = implode('', $contents);
626 $dir = implode('', $ctrldir);
627 $buffer = $data.$dir."\x50\x4b\x05\x06\x00\x00\x00\x00".pack('v', count($ctrldir)).pack('v', count($ctrldir)).pack(
628 'V',
629 strlen($dir)
630 ).pack('V', strlen($data))."\x00\x00";
631
632 return acym_writeFile($name.'.zip', $buffer);
633 }
634
635 function acym_loaderLogo(bool $withContainer = true): void
636 {
637 if ($withContainer) {
638 echo '<div class="cell shrink acym_loader_logo">';
639 }
640 ?>
641 <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="80" height="80" viewBox="0 0 1024 1024">
642 <path class="acym__svg__loader"
643 fill="#a6a9ab"
644 d="M553.074 174.168c-12.201 7.319-26.84 10.573-40.668 10.573s-27.655-3.253-40.668-10.573l-242.376-139.081-229.362 132.575v732.011l254.576 143.963v-430.26l219.602 124.442c11.388 6.507 24.401 9.76 37.415 9.76 0 0 0 0 0 0s0 0 0 0c12.201 0 25.214-3.253 36.6-9.76l221.23-124.442v430.26l254.576-144.775v-732.011l-229.362-131.762-241.563 139.081zM491.261 701.215l-217.164-122.815-61.001-34.161v430.26l-173.243-97.601v-662.063l422.94 245.629c0 0 0 0 0 0 8.947 4.881 17.895 8.135 27.655 10.573v230.178zM983.334 876.086l-173.243 98.416v-431.073l-61.001 34.161-217.164 122.815v-229.362c9.76-2.441 18.707-5.694 27.655-10.573l423.753-246.444v662.063zM539.246 425.493c-17.080 9.76-38.227 9.76-55.307 0 0 0 0 0 0 0l-422.94-245.629 168.362-97.601 222.043 127.696c18.707 10.573 39.853 16.267 61.001 16.267s42.294-5.694 61.001-16.267l222.043-127.696 168.362 97.601-424.566 245.629z"></path>
645 </svg>
646 <?php
647 if ($withContainer) {
648 echo '</div>';
649 }
650 }
651
652 function acym_fileNameValid(string $filename): bool
653 {
654 if (empty($filename)) return false;
655
656 if (strpos($filename, '..') !== false || strpos($filename, '/') !== false || strpos($filename, '\\') !== false) return false;
657
658 return true;
659 }
660
661 function acym_getImageFileExtensions(bool $allowSvg = false): array
662 {
663 $imageFormats = ['jpg', 'jpeg', 'png', 'gif', 'ico', 'bmp'];
664 if ($allowSvg) {
665 $imageFormats[] = 'svg';
666 }
667
668 return $imageFormats;
669 }
670
671 function acym_isSvgFileSafe(string $path): bool
672 {
673 if (!is_file($path)) {
674 return false;
675 }
676
677 $content = @file_get_contents($path);
678 if ($content === false || trim($content) === '') {
679 return false;
680 }
681
682 // Decode HTML entities so obfuscated payloads (e.g. "javascript&#58;") are caught, and drop NULL bytes.
683 $content = str_replace("\0", '', html_entity_decode($content, ENT_QUOTES | ENT_HTML5));
684
685 $dangerousPatterns = [
686 '#<\s*script#i',
687 '#<\s*(foreignObject|iframe|embed|object|audio|video|handler|listener)#i',
688 '#\son[a-z]+\s*=#i',
689 '#javascript\s*:#i',
690 '#data\s*:\s*text/html#i',
691 '#<!\s*(DOCTYPE|ENTITY)#i',
692 ];
693
694 foreach ($dangerousPatterns as $pattern) {
695 if (preg_match($pattern, $content)) {
696 return false;
697 }
698 }
699
700 return true;
701 }
702