<?php

use AcyMailing\Helpers\ImageHelper;

function acym_bytes(string $val): int
{
    $val = trim($val);
    if (empty($val)) {
        return 0;
    }
    $last = strtolower($val[strlen($val) - 1]);
    switch ($last) {
        case 'g':
            $val = intval($val) * 1073741824;
            break;
        case 'm':
            $val = intval($val) * 1048576;
            break;
        case 'k':
            $val = intval($val) * 1024;
            break;
    }

    return (int)$val;
}

function acym_createDir(string $dir, bool $report = true, bool $secured = false): bool
{
    if (is_dir($dir)) {
        return true;
    }

    $indexhtml = '<html><body bgcolor="#FFFFFF"></body></html>';

    try {
        $status = acym_createFolder($dir);
    } catch (Exception $e) {
        $status = false;
    }

    if (!$status) {
        if ($report) {
            acym_display('Could not create the directory '.$dir, 'error');
        }

        return false;
    }

    try {
        $status = acym_writeFile($dir.DS.'index.html', $indexhtml);
    } catch (Exception $e) {
        $status = false;
    }

    if (!$status && $report) {
        acym_display('Could not create the file '.$dir.DS.'index.html', 'error');
    }

    if ($secured) {
        try {
            $htaccess = 'Order deny,allow'."\r\n".'Deny from all';
            $status = acym_writeFile($dir.DS.'.htaccess', $htaccess);
        } catch (Exception $e) {
            $status = false;
        }

        if (!$status && $report) {
            acym_display('Could not create the file '.$dir.DS.'.htaccess', 'error');
        }
    }

    return $status;
}

function acym_importFile(array $file, string $uploadPath, bool $onlyPict): ?string
{
    acym_checkToken();

    $config = acym_config();
    $additionalMsg = '';

    if ($file['error'] > 0) {
        $file['error'] = intval($file['error']);
        if ($file['error'] > 8) {
            $file['error'] = 0;
        }

        $phpFileUploadErrors = [
            0 => 'Unknown error',
            1 => 'The uploaded file exceeds the upload_max_filesize directive in php.ini',
            2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
            3 => 'The uploaded file was only partially uploaded',
            4 => 'No file was uploaded',
            6 => 'Missing a temporary folder',
            7 => 'Failed to write file to disk',
            8 => 'A PHP extension stopped the file upload',
        ];

        acym_enqueueMessage(acym_translationSprintf('ACYM_ERROR_UPLOADING_FILE_X', $phpFileUploadErrors[$file['error']]), 'error');

        return null;
    }

    acym_createDir($uploadPath, true);

    if (!is_writable($uploadPath)) {
        @chmod($uploadPath, '0755');
        if (!is_writable($uploadPath)) {
            acym_display(acym_translationSprintf('ACYM_WRITABLE_FOLDER', $uploadPath), 'error');

            return null;
        }
    }

    if ($onlyPict) {
        $allowedExtensions = ['png', 'jpeg', 'jpg', 'gif', 'ico', 'bmp'];
    } else {
        $allowedExtensions = explode(',', $config->get('allowed_files'));
    }

    if (!preg_match('#\.('.implode('|', $allowedExtensions).')$#Ui', $file['name'], $extension)) {
        $ext = substr($file['name'], strrpos($file['name'], '.') + 1);
        acym_display(
            acym_translationSprintf(
                'ACYM_ACCEPTED_TYPE',
                acym_escape($ext),
                implode(', ', $allowedExtensions)
            ),
            'error'
        );

        return null;
    }

    if (preg_match('#\.(php.?|.?htm.?|pl|py|jsp|asp|sh|cgi)#Ui', $file['name'])) {
        acym_display(
            'This extension name is blocked by the system regardless your configuration for security reasons',
            'error'
        );

        return null;
    }

    $file['name'] = str_replace(
            ['-', '.', ' ', '\''],
            '_',
            substr($file['name'], 0, strrpos($file['name'], '.'))
        ).'.'.$extension[1];

    if ($onlyPict) {
        $imageSize = @getimagesize($file['tmp_name']);
        if (empty($imageSize)) {
            acym_display(acym_translation('ACYM_INVALID_IMAGE'), 'error');

            return null;
        }
    }

    if (file_exists($uploadPath.DS.$file['name'])) {
        $i = 1;
        $nameFile = preg_replace('/\\.[^.\\s]{3,4}$/', '', $file['name']);
        $ext = substr($file['name'], strrpos($file['name'], '.') + 1);
        while (file_exists($uploadPath.DS.$nameFile.'_'.$i.'.'.$ext)) {
            $i++;
        }

        $file['name'] = $nameFile.'_'.$i.'.'.$ext;
        $additionalMsg = '<br />'.acym_translationSprintf('ACYM_FILE_RENAMED', $file['name']);
        if ($onlyPict) {
            $additionalMsg .= '<br /><a style="color: blue; cursor: pointer;" onclick="confirmBox(\'rename\', \''.$file['name'].'\', \''.$nameFile.'.'.$ext.'\')">'.acym_translation(
                    'ACYM_RENAME_OR_REPLACE'
                ).'</a>';
        }
    }

    if (!acym_uploadFile($file['tmp_name'], rtrim($uploadPath, DS).DS.$file['name'])) {
        if (!move_uploaded_file($file['tmp_name'], rtrim($uploadPath, DS).DS.$file['name'])) {
            acym_display(
                acym_translationSprintf(
                    'ACYM_FAIL_UPLOAD',
                    '<b><i>'.acym_escape($file['tmp_name']).'</i></b>',
                    '<b><i>'.acym_escape(rtrim($uploadPath, DS).DS.$file['name']).'</i></b>'
                ),
                'error'
            );

            return null;
        }
    }

    if ($onlyPict && $imageSize[0] > 1000) {
        $imageHelper = new ImageHelper();
        if ($imageHelper->available()) {
            $imageHelper->maxHeight = 9999;
            $imageHelper->maxWidth = 700;
            $message = 'ACYM_IMAGE_RESIZED';
            $imageHelper->destination = $uploadPath;
            $thumb = $imageHelper->generateThumbnail(rtrim($uploadPath, DS).DS.$file['name']);
            if (!empty($thumb)) {
                acym_moveFile($thumb['file'], $uploadPath.DS.$file['name']);
                $additionalMsg .= '<br />'.acym_translation($message);
            }
        }
    }
    acym_enqueueMessage(acym_translation('ACYM_SUCCESS_FILE_UPLOAD').$additionalMsg, 'success');

    return $file['name'];
}

function acym_inputFile(string $name, string $value = '', string $class = '', string $attributes = '', string $downloadUrl = ''): string
{
    $hasValue = !empty($value);
    $actionStyle = $hasValue ? '' : ' style="display: none;"';
    $noFileText = acym_translation('ACYM_NO_FILE_CHOSEN');
    $baseName = substr($name, -2) === '[]' ? substr($name, 0, -2) : $name;

    $downloadAttr = !empty($downloadUrl) ? ' data-download-url="'.acym_escapeUrl($downloadUrl).'"' : '';

    $return = '</label><div class="cell '.acym_escape($class).' grid-x acym__input__file__container">';
    $return .= '<input type="hidden" name="'.acym_escape($baseName).'"  value="'.acym_escape($value).'" class="acym__input__file__clear">';
    $return .= '<input '.$attributes.' style="display: none" type="file" name="'.acym_escape($name).'">';
    $return .= '<button type="button" class="acym__button__file button button-secondary cell shrink">'.acym_translation('ACYM_CHOOSE_FILE').'</button>';
    $return .= '<span class="cell shrink margin-left-2 margin-right-2 acym__input__file__name" data-no-file="'.acym_escape($noFileText).'">';
    $return .= acym_escape($hasValue ? $value : $noFileText);
    $return .= '</span>';
    $return .= '<i class="acym__input__file__download cell shrink acym__color__blue cursor-pointer acymicon-download margin-right-1"'.$actionStyle.$downloadAttr.
        ' title="'.acym_escape(acym_translation('ACYM_DOWNLOAD')).'" aria-label="'.acym_escape(acym_translation('ACYM_DOWNLOAD')).'"></i>';
    $return .= '<i class="acymicon-close acym__color__red acym__input__file__delete cursor-pointer cell shrink margin-left-1"'.$actionStyle.
        ' title="'.acym_escape(acym_translation('ACYM_DELETE')).'" aria-label="'.acym_escape(acym_translation('ACYM_DELETE')).'"></i>';
    $return .= '</div>';

    return $return;
}

function acym_getFilesFolder(bool $ignoreVariables = false): string
{
    $config = acym_config();
    $uploadFolder = $config->get('uploadfolder', ACYM_UPLOAD_FOLDER);
    if ($ignoreVariables) {
        $uploadFolder = str_replace(['{userid}', '{groupname}', '{year}', '{month}', '{day}'], '', $uploadFolder);
    }
    $uploadFolder = trim($uploadFolder, '/');

    $uploadFolder = str_replace('{userid}', acym_currentUserId(), $uploadFolder);
    $uploadFolder = str_replace('{year}', date('Y'), $uploadFolder);
    $uploadFolder = str_replace('{month}', date('m'), $uploadFolder);
    $uploadFolder = str_replace('{day}', date('d'), $uploadFolder);

    return acym_replaceGroupTags($uploadFolder);
}

function acym_generateArborescence(array $folders): array
{
    $folderList = [];
    foreach ($folders as $folder) {
        $folderPath = acym_cleanPath(ACYM_ROOT.trim(str_replace('/', DS, trim($folder)), DS));
        if (!file_exists($folderPath)) {
            acym_createDir($folderPath);
        }
        $subFolders = acym_listFolderTree($folderPath, '', 15);
        $folderList[$folder] = [];
        foreach ($subFolders as $oneFolder) {
            $subFolder = str_replace(ACYM_ROOT, '', $oneFolder['relname']);
            $subFolder = str_replace(DS, '/', $subFolder);
            $folderList[$folder][$subFolder] = ltrim($subFolder, '/');
        }
        $folderList[$folder] = array_unique($folderList[$folder]);
    }

    return $folderList;
}

function acym_makeSafeFile(string $file): string
{
    $file = rtrim($file, '.');
    $regex = ['#(\.){2,}#', '#[^A-Za-z0-9\.\_\- ]#', '#^\.#'];

    return trim(preg_replace($regex, '', $file));
}

function acym_deleteFolder(string $path, bool $report = true): bool
{
    $path = acym_cleanPath($path);
    if (!is_dir($path)) {
        if ($report) acym_enqueueMessage(acym_translationSprintf('ACYM_IS_NOT_A_FOLDER', $path), 'error');

        return false;
    }

    $files = acym_getFiles($path, '.', false, false, [], []);
    if (!empty($files)) {
        foreach ($files as $oneFile) {
            if (!acym_deleteFile($path.DS.$oneFile, $report)) {
                return false;
            }
        }
    }

    $folders = acym_getFolders($path, '.', false, false, []);
    if (!empty($folders)) {
        foreach ($folders as $oneFolder) {
            if (!acym_deleteFolder($path.DS.$oneFolder, $report)) {
                return false;
            }
        }
    }

    if (@rmdir($path)) {
        return true;
    } else {
        if ($report) acym_enqueueMessage(acym_translationSprintf('ACYM_COULD_NOT_DELETE_FOLDER', $path), 'error');

        return false;
    }
}

function acym_createFolder(string $path = '', int $mode = 0755): bool
{
    $path = acym_cleanPath($path);
    if (file_exists($path)) {
        return true;
    }

    $origmask = @umask(0);
    $ret = @mkdir($path, $mode, true);
    @umask($origmask);

    return $ret;
}

function acym_getFolders(
    string $path,
    string $filter = '.',
    bool   $recurse = false,
    bool   $full = false,
    array  $exclude = ['.svn', 'CVS', '.DS_Store', '__MACOSX'],
    array  $excludefilter = ['^\..*']
): array {
    $path = acym_cleanPath($path);

    if (!is_dir($path)) {
        acym_enqueueMessage(acym_translationSprintf('ACYM_IS_NOT_A_FOLDER', $path), 'error');

        return [];
    }

    if (count($excludefilter)) {
        $excludefilter_string = '/('.implode('|', $excludefilter).')/';
    } else {
        $excludefilter_string = '';
    }

    $arr = acym_getItems($path, $filter, $recurse, $full, $exclude, $excludefilter_string, false);
    asort($arr);

    return array_values($arr);
}

function acym_getFiles(
    string $path,
    string $filter = '.',
    bool   $recurse = false,
    bool   $full = false,
    array  $exclude = ['.svn', 'CVS', '.DS_Store', '__MACOSX'],
    array  $excludefilter = [
        '^\..*',
        '.*~',
    ],
    bool   $naturalSort = false
): array {
    $path = acym_cleanPath($path);

    if (!is_dir($path)) {
        acym_enqueueMessage(acym_translationSprintf('ACYM_IS_NOT_A_FOLDER', $path), 'error');

        return [];
    }

    if (count($excludefilter)) {
        $excludefilter_string = '/('.implode('|', $excludefilter).')/';
    } else {
        $excludefilter_string = '';
    }

    $arr = acym_getItems($path, $filter, $recurse, $full, $exclude, $excludefilter_string, true);

    if ($naturalSort) {
        natsort($arr);
    } else {
        asort($arr);
    }

    return array_values($arr);
}

function acym_getItems(
    string $path,
    string $filter,
    bool   $recurse,
    bool   $full,
    array  $exclude,
    string $excludefilter_string,
    bool   $findfiles
): array {
    $arr = [];

    if (!($handle = @opendir($path))) {
        return $arr;
    }

    while (($file = readdir($handle)) !== false) {
        if (
            $file == '.'
            || $file == '..'
            || in_array($file, $exclude)
            || (!empty($excludefilter_string) && preg_match($excludefilter_string, $file))
        ) {
            continue;
        }
        $fullpath = rtrim($path, '/').'/'.$file;

        $isDir = is_dir($fullpath);

        if (($isDir xor $findfiles) && preg_match("/$filter/", $file)) {
            if ($full) {
                $arr[] = $fullpath;
            } else {
                $arr[] = $file;
            }
        }

        if ($isDir && $recurse) {
            $arr = array_merge(
                $arr,
                acym_getItems(
                    $fullpath,
                    $filter,
                    true,
                    $full,
                    $exclude,
                    $excludefilter_string,
                    $findfiles
                )
            );
        }
    }

    closedir($handle);

    return $arr;
}

function acym_copyFolder(string $src, string $dest, ?string $path = null, bool $force = false, bool $use_streams = false): bool
{
    if (!empty($path)) {
        $src = acym_cleanPath($path.'/'.$src);
        $dest = acym_cleanPath($path.'/'.$dest);
    }

    $src = rtrim($src, DIRECTORY_SEPARATOR);
    $dest = rtrim($dest, DIRECTORY_SEPARATOR);

    if (!file_exists($src)) {
        acym_enqueueMessage(acym_translationSprintf('ACYM_FOLDER_DOES_NOT_EXIST', $src), 'error');

        return false;
    }

    if (file_exists($dest) && !$force) {
        acym_enqueueMessage(acym_translationSprintf('ACYM_FOLDER_ALREADY_EXIST', $dest), 'error');

        return true;
    }

    if (!acym_createFolder($dest)) {
        acym_enqueueMessage(acym_translation('ACYM_CANNOT_CREATE_DESTINATION_FOLDER'), 'error');

        return false;
    }

    if (!($dh = @opendir($src))) {
        acym_enqueueMessage(acym_translation('ACYM_CANNOT_OPEN_SOURCE_FOLDER'), 'error');

        return false;
    }

    while (($file = readdir($dh)) !== false) {
        $sfid = $src.'/'.$file;
        $dfid = $dest.'/'.$file;

        switch (filetype($sfid)) {
            case 'dir':
                if ($file != '.' && $file != '..') {
                    $ret = acym_copyFolder($sfid, $dfid, null, $force, $use_streams);

                    if ($ret !== true) {
                        return $ret;
                    }
                }
                break;

            case 'file':
                if (!@copy($sfid, $dfid)) {
                    acym_enqueueMessage(acym_translationSprintf('ACYM_COPY_FILE_FAILED_PERMISSION', $sfid), 'error');

                    return false;
                }
                break;
        }
    }

    return true;
}

function acym_listFolderTree(string $path, string $filter, int $maxLevel = 3, int $level = 0, int $parent = 0): array
{
    $dirs = [];

    if ($level === 0) {
        $GLOBALS['acym_folder_tree_index'] = 0;
    }

    if ($level < $maxLevel) {
        $folders = acym_getFolders($path, $filter);

        foreach ($folders as $name) {
            $id = ++$GLOBALS['acym_folder_tree_index'];
            $fullName = acym_cleanPath($path.'/'.$name);
            $dirs[] = [
                'id' => $id,
                'parent' => $parent,
                'name' => $name,
                'fullname' => $fullName,
                'relname' => str_replace(ACYM_ROOT, '', $fullName),
            ];
            $dirs2 = acym_listFolderTree($fullName, $filter, $maxLevel, $level + 1, $id);
            $dirs = array_merge($dirs, $dirs2);
        }
    }

    return $dirs;
}

function acym_deleteFile(string $file, bool $report = true): bool
{
    $file = acym_cleanPath($file);
    if (!is_file($file)) {
        if ($report) acym_enqueueMessage(acym_translationSprintf('ACYM_IS_NOT_A_FILE', $file), 'error');

        return false;
    }

    @chmod($file, 0777);

    if (!@unlink($file)) {
        $filename = basename($file);
        if ($report) acym_enqueueMessage(acym_translationSprintf('ACYM_FAILED_DELETE', $filename), 'error');

        return false;
    }

    return true;
}

function acym_writeFile(string $file, $buffer, int $flags = 0): bool
{
    if (!file_exists(dirname($file)) && !acym_createFolder(dirname($file))) {
        return false;
    }

    $file = acym_cleanPath($file);

    return is_int(file_put_contents($file, $buffer, $flags));
}

function acym_moveFile(string $src, string $dest, string $path = ''): bool
{
    if (!empty($path)) {
        $src = acym_cleanPath($path.'/'.$src);
        $dest = acym_cleanPath($path.'/'.$dest);
    }

    if (!is_readable($src)) {
        acym_enqueueMessage(acym_translationSprintf('ACYM_COULD_NOT_FIND_FILE_SOURCE_PERMISSION', $src), 'error');

        return false;
    }

    if (!@rename($src, $dest)) {
        acym_enqueueMessage(acym_translation('ACYM_COULD_NOT_MOVE_FILE'), 'error');

        return false;
    }

    return true;
}

function acym_uploadFile(string $src, string $dest): bool
{
    $dest = acym_cleanPath($dest);

    $baseDir = dirname($dest);
    if (!file_exists($baseDir)) {
        acym_createFolder($baseDir);
    }

    if (is_writeable($baseDir) && move_uploaded_file($src, $dest)) {
        if (@chmod($dest, octdec('0644'))) {
            return true;
        } else {
            acym_enqueueMessage(acym_translation('ACYM_FILE_REJECTED_SAFETY_REASON'), 'error');
        }
    } else {
        acym_enqueueMessage(acym_translationSprintf('ACYM_COULD_NOT_UPLOAD_FILE_PERMISSION', $baseDir), 'error');
    }

    return false;
}

function acym_copyFile(string $src, string $dest, string $path = ''): bool
{
    if (!empty($path)) {
        $src = acym_cleanPath($path.DS.$src);
        $dest = acym_cleanPath($path.DS.$dest);
    }

    if (!is_readable($src)) {
        acym_enqueueMessage(acym_translationSprintf('ACYM_COULD_NOT_FIND_FILE_SOURCE_PERMISSION', $src), 'error');

        return false;
    }

    if (!@copy($src, $dest)) {
        acym_enqueueMessage(acym_translationSprintf('ACYM_COULD_NOT_COPY_FILE_X_TO_X', $src, $dest), 'error');

        return false;
    }

    return true;
}

function acym_fileGetExt(string $file): string
{
    $pathInfo = pathinfo($file);

    return empty($pathInfo['extension']) ? '' : strtolower($pathInfo['extension']);
}

function acym_cleanPath(string $path, string $ds = DIRECTORY_SEPARATOR): string
{
    $path = trim($path);

    if (empty($path)) {
        $path = ACYM_ROOT;
    } elseif (($ds === '\\') && substr($path, 0, 2) == '\\\\') {
        $path = "\\".preg_replace('#[/\\\\]+#', $ds, $path);
    } else {
        $path = preg_replace('#[/\\\\]+#', $ds, $path);
    }

    return $path;
}

function acym_createArchive(string $name, array $files): bool
{
    $contents = [];
    $ctrldir = [];

    $timearray = getdate();
    $dostime = (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) | ($timearray['hours'] << 11) | ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1);
    $dtime = dechex($dostime);
    $hexdtime = chr(hexdec($dtime[6].$dtime[7])).chr(hexdec($dtime[4].$dtime[5])).chr(hexdec($dtime[2].$dtime[3])).chr(
            hexdec($dtime[0].$dtime[1])
        );

    foreach ($files as $file) {
        $data = $file['data'];
        $filename = str_replace('\\', '/', $file['name']);

        $fr = "\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00".$hexdtime;

        $unc_len = strlen($data);
        $crc = crc32($data);
        $zdata = gzcompress($data);
        $zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2);
        $c_len = strlen($zdata);

        $fr .= pack('V', $crc).pack('V', $c_len).pack('V', $unc_len).pack('v', strlen($filename)).pack(
                'v',
                0
            ).$filename.$zdata;

        $old_offset = strlen(implode('', $contents));
        $contents[] = $fr;

        $cdrec = "\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00".$hexdtime;
        $cdrec .= pack('V', $crc).pack('V', $c_len).pack('V', $unc_len).pack('v', strlen($filename)).pack('v', 0).pack(
                'v',
                0
            ).pack('v', 0).pack('v', 0).pack('V', 32).pack('V', $old_offset).$filename;

        $ctrldir[] = $cdrec;
    }

    $data = implode('', $contents);
    $dir = implode('', $ctrldir);
    $buffer = $data.$dir."\x50\x4b\x05\x06\x00\x00\x00\x00".pack('v', count($ctrldir)).pack('v', count($ctrldir)).pack(
            'V',
            strlen($dir)
        ).pack('V', strlen($data))."\x00\x00";

    return acym_writeFile($name.'.zip', $buffer);
}

function acym_loaderLogo(bool $wrap = true): string
{
    $loader = '';
    if ($wrap) {
        $loader .= '<div class="cell shrink acym_loader_logo">';
    }

    $loader .= '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="80" height="80" viewBox="0 0 1024 1024">
	                <path class="acym__svg__loader" fill="#a6a9ab" 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>
                </svg>';

    if ($wrap) {
        $loader .= '</div>';
    }

    return $loader;
}

function acym_fileNameValid(string $filename): bool
{
    if (empty($filename)) return false;

    if (strpos($filename, '..') !== false || strpos($filename, '/') !== false || strpos($filename, '\\') !== false) return false;

    return true;
}

function acym_getImageFileExtensions(): array
{
    return ['jpg', 'jpeg', 'png', 'gif', 'ico', 'bmp', 'svg'];
}
