<?php
/*
  RoxyFileman - web based file manager. Ready to use with CKEditor, TinyMCE.
  Can be easily integrated with any other WYSIWYG editor or CMS.

  Copyright (C) 2013, RoxyFileman.com - Lyubomir Arsov. All rights reserved.
  For licensing, see LICENSE.txt or http://RoxyFileman.com/license

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.

  Contact: Lyubomir Arsov, liubo (at) web-lobby.com
*/
include_once 'security.inc.php';

function t(string $key): string
{
    global $LANG;

    if (empty($LANG)) {
        $file = 'en.json';
        $langPath = '../lang/';
        if (defined('LANG')) {
            if (LANG == 'auto') {
                $lang = strtolower(substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2));
                if (is_file($langPath . $lang . '.json')) {
                    $file = $lang . '.json';
                }
            } elseif (is_file($langPath . LANG . '.json')) {
                $file = LANG . '.json';
            }
        }
        $file = $langPath . $file;
        $LANG = json_decode((string) file_get_contents($file), true);
    }

    if (!$LANG[$key]) {
        $LANG[$key] = $key;
    }

    return $LANG[$key];
}

function checkPath(string $path): bool
{
    return mb_strpos($path . '/', getFilesPath()) === 0;
}

function verifyAction(string $action): void
{
    if (!defined($action) || !constant($action)) {
        exit;
    }

    $confUrl = constant($action);
    if (!is_string($confUrl)) {
        die('Error parsing configuration');
    }
    $qStr = mb_strpos($confUrl, '?');
    if ($qStr !== false) {
        $confUrl = mb_substr($confUrl, 0, $qStr);
    }
    $confUrl = BASE_PATH . '/' . $confUrl;
    $confUrl = RoxyFile::FixPath($confUrl);
    $thisUrl = dirname(__FILE__) . '/' . basename($_SERVER['PHP_SELF']);
    $thisUrl = RoxyFile::FixPath($thisUrl);
    if ($thisUrl != $confUrl) {
        echo "$confUrl $thisUrl";
        exit;
    }
}

function verifyPath(string $path): void
{
    if (!checkPath($path)) {
        echo getErrorRes("Access to $path is denied") . ' ' . $path;
        exit;
    }
}

function fixPath(string $path): string
{
    $path = dirname($_SERVER['SCRIPT_FILENAME']) . '/../../../../../../' . $path;

    $re = '/\/[^\/.]*\/\.\.\//mU';
    while(preg_match($re, $path)) {
        $path = preg_replace($re, "/", $path);
    }

    $path = str_replace('\\', '/', $path);
    $path = RoxyFile::FixPath($path);
    return $path;
}

function getResultStr(string $type, string $str = ''): string
{
    return '{"res":"' . addslashes($type) . '","msg":"' . addslashes($str) . '"}';
}

function getSuccessRes(string $str = ''): string
{
    return getResultStr('ok', $str);
}

function getErrorRes(string $str = ''): string
{
    return getResultStr('error', $str);
}

function getFilesPath(): string
{
    $ret = (isset($_SESSION[SESSION_PATH_KEY]) && $_SESSION[SESSION_PATH_KEY] != '' ? $_SESSION[SESSION_PATH_KEY] : FILES_ROOT);

    if (!$ret) {
        $ret = RoxyFile::FixPath(BASE_PATH . '/Uploads');
        $tmp = $_SERVER['DOCUMENT_ROOT'];
        if (mb_substr($tmp, -1) == '/' || mb_substr($tmp, -1) == '\\') {
            $tmp = mb_substr($tmp, 0, -1);
        }
        $ret = str_replace(RoxyFile::FixPath($tmp), '', $ret);
    }
    return $ret;
}

/**
 * @param string $path
 * @return string[]
 */
function listDirectory(string $path): array
{
    $ret = @scandir($path);
    if ($ret === false) {
        $ret = [];
        $d = opendir($path);
        if ($d) {
            while (($f = readdir($d)) !== false) {
                $ret[] = $f;
            }
            closedir($d);
        }
    }

    return $ret;
}

class RoxyFile
{
    public static function CheckWritable(string $dir): bool
    {
        $ret = false;
        if (self::CreatePath($dir)) {
            $dir = self::FixPath($dir . '/');
            $testFile = 'writetest.txt';
            $f = @fopen($dir . $testFile, 'w', false);
            if ($f) {
                fclose($f);
                $ret = true;
                @unlink($dir . $testFile);
            }
        }

        return $ret;
    }

    /**
     * @param string $path
     * @return bool
     */
    public static function CreatePath(string $path): bool
    {
        if (is_dir($path)) {
            return true;
        }

        $prev_path = substr($path, 0, strrpos($path, '/', -2) + 1);
        $return = self::CreatePath( $prev_path);
        return $return && is_writable($prev_path) && mkdir($path);
    }

    public static function CanUploadFile(string $filename): bool
    {
        $forbidden = array_filter((array) preg_split('/[^\d\w]+/', strtolower(FORBIDDEN_UPLOADS)));
        $allowed = array_filter((array) preg_split('/[^\d\w]+/', strtolower(ALLOWED_UPLOADS)));
        $ext = RoxyFile::GetExtension($filename);

        if ((empty($forbidden) || !in_array($ext, $forbidden)) && (empty($allowed) || in_array($ext, $allowed))) {
            return true;
        }

        return false;
    }

    public static function ZipAddDir(string $path, ZipArchive $zip, string $zipPath): void
    {
        $d = opendir($path);
        $zipPath = str_replace('//', '/', $zipPath);
        if ($zipPath && $zipPath != '/') {
            $zip->addEmptyDir($zipPath);
        }
        if (is_resource($d)) {
            while (($f = readdir($d)) !== false) {
                if ($f == '.' || $f == '..') {
                    continue;
                }
                $filePath = $path . '/' . $f;
                if (is_file($filePath)) {
                    $zip->addFile($filePath, ($zipPath ? $zipPath . '/' : '') . $f);
                } elseif (is_dir($filePath)) {
                    self::ZipAddDir($filePath, $zip, ($zipPath ? $zipPath . '/' : '') . $f);
                }
            }
        }
        if (is_resource($d)) {
            closedir($d);
        }
    }

    public static function ZipDir(string $path, string $zipFile, string $zipPath = ''): void
    {
        $zip = new ZipArchive();
        $zip->open($zipFile, ZIPARCHIVE::CREATE);
        self::ZipAddDir($path, $zip, $zipPath);
        $zip->close();
    }

    public static function IsImage(string $fileName): bool
    {
        $ext = strtolower(self::GetExtension($fileName));

        $imageExtensions = ['jpg', 'jpeg', 'jpe', 'png', 'gif', 'ico', 'webp'];

        return in_array($ext, $imageExtensions);
    }

    public static function IsFlash(string $fileName): bool
    {
        $ext = strtolower(self::GetExtension($fileName));

        $flashExtensions = ['swf', 'flv', 'swc', 'swt'];

        return in_array($ext, $flashExtensions);
    }

    /**
     * Returns human formated file size
     *
     * @param int $filesize
     * @return string
     */
    public static function FormatFileSize(int $filesize): string
    {
        $unit = 'B';
        if ($filesize > 1024) {
            $unit = 'KB';
            $filesize = $filesize / 1024;
        }
        if ($filesize > 1024) {
            $unit = 'MB';
            $filesize = $filesize / 1024;
        }
        if ($filesize > 1024) {
            $unit = 'GB';
            $filesize = $filesize / 1024;
        }

        $ret = round($filesize, 2) . ' ' . $unit;
        return $ret;
    }

    /**
     * Returns MIME type of $filename
     *
     * @param string $filename
     * @return string
     */
    public static function GetMIMEType(string $filename): string
    {
        $ext = self::GetExtension($filename);

        switch (strtolower($ext)) {
            case 'jpg':
            case 'jpeg':
                return 'image/jpeg';
            case 'gif':
                return 'image/gif';
            case 'png':
                return 'image/png';
            case 'bmp':
                return 'image/bmp';
            case 'webp':
                return 'image/webp';
            case 'tiff':
            case 'tif':
                return 'image/tiff';
            case 'pdf':
                return 'application/pdf';
            case 'rtf':
            case 'doc':
                return 'application/msword';
            case 'xls':
                return 'application/vnd.ms-excel';
            case 'zip':
                return 'application/zip';
            case 'swf':
                return 'application/x-shockwave-flash';
            default:
                return 'application/octet-stream';
        }
    }

    /**
     * Replaces any character that is not letter, digit or underscore from $filename with $sep
     *
     * @param string $filename
     * @param string $sep
     * @return string
     */
    public static function CleanupFilename(string $filename, string $sep = '_'): string
    {
        $str = '';
        if (strpos($filename, '.')) {
            $ext = self::GetExtension($filename);
            $name = self::GetName($filename);
        } else {
            $ext = '';
            $name = $filename;
        }
        if (mb_strlen($name) > 32) {
            $name = mb_substr($name, 0, 32);
        }
        $str = str_replace('.php', '', $str);
        $str = (string) mb_ereg_replace("[^\\w]", $sep, $name);

        $str = (string) mb_ereg_replace("$sep+", $sep, $str) . ($ext ? '.' . $ext : '');

        return $str;
    }

    /**
     * Returns file extension without dot
     *
     * @param string $filename
     * @return string
     */
    public static function GetExtension(string $filename): string
    {
        $ext = '';

        if (mb_strrpos($filename, '.') !== false) {
            $ext = mb_substr($filename, mb_strrpos($filename, '.') + 1);
        }

        return strtolower($ext);
    }

    /**
     * Returns file name without extension
     *
     * @param string $filename
     * @return string
     */
    public static function GetName(string $filename): string
    {
        $tmp = mb_strpos($filename, '?');
        if ($tmp !== false) {
            $filename = mb_substr($filename, 0, $tmp);
        }
        $dotPos = mb_strrpos($filename, '.');
        if ($dotPos !== false) {
            $name = mb_substr($filename, 0, $dotPos);
        } else {
            $name = $filename;
        }

        return $name;
    }

    public static function GetFullName(string $filename): string
    {
        $tmp = mb_strpos($filename, '?');
        if ($tmp !== false) {
            $filename = mb_substr($filename, 0, $tmp);
        }
        return basename($filename);
    }

    public static function FixPath(string $path): string
    {
        $path = (string) mb_ereg_replace('[\\\/]+', '/', $path);
        //$path = (string) mb_ereg_replace('\.\.\/', '', $path);

        return $path;
    }

    /**
     * creates unique file name using $filename( " - Copy " and number is added if file already exists) in directory $dir
     *
     * @param string $dir
     * @param string $filename
     * @return string
     */
    public static function MakeUniqueFilename(string $dir, string $filename): string
    {
        ;
        $dir .= '/';
        $dir = self::FixPath($dir . '/');
        $ext = self::GetExtension($filename);
        $name = self::GetName($filename);
        $name = self::CleanupFilename($name);
        $name = mb_ereg_replace(' \\- Copy \\d+$', '', $name);
        if ($ext) {
            $ext = '.' . $ext;
        }
        if (!$name) {
            $name = 'file';
        }

        $i = 0;
        do {
            $temp = ($i > 0 ? $name . " - Copy $i" : $name) . $ext;
            $i++;
        } while (file_exists($dir . $temp));

        return $temp;
    }

    /**
     * creates unique directory name using $name( " - Copy " and number is added if directory already exists) in directory $dir
     *
     * @param string $dir
     * @param string $name
     * @return string
     */
    public static function MakeUniqueDirname(string $dir, string $name): string
    {
        $dir = self::FixPath($dir . '/');
        $name = mb_ereg_replace(' - Copy \\d+$', '', $name);
        if (!$name) {
            $name = 'directory';
        }

        $i = 0;
        do {
            $temp = ($i ? $name . " - Copy $i" : $name);
            $i++;
        } while (is_dir($dir . $temp));

        return $temp;
    }
}
class RoxyImage
{
    public static function GetImage(string $path)
    {
        $ext = RoxyFile::GetExtension(basename($path));
        switch ($ext) {
            case 'png':
                return imagecreatefrompng($path);
            case 'gif':
                return imagecreatefromgif($path);
            default:
                return imagecreatefromjpeg($path);
        }
    }

    public static function OutputImage($img, string $type, ?string $destination = '', int $quality = 90)
    {
        if (is_string($img)) {
            $img = self::GetImage($img);
        }

        switch (strtolower($type)) {
            case 'png':
                imagepng($img, $destination);
                break;
            case 'gif':
                imagegif($img, $destination);
                break;
            default:
                imagejpeg($img, $destination, $quality);
        }
    }

    public static function SetAlpha($img, string $path)
    {
        $ext = RoxyFile::GetExtension(basename($path));
        if ($ext == "gif" || $ext == "png") {
            imagecolortransparent($img, (int) imagecolorallocatealpha($img, 0, 0, 0, 127));
            imagealphablending($img, false);
            imagesavealpha($img, true);
        }

        return $img;
    }

    public static function Resize(
        string $source,
        ?string $destination,
        int $width = 150,
        int $height = 0,
        int $quality = 90
    ): void {
        $tmp = (array) getimagesize($source);
        $w = $tmp[0];
        $h = $tmp[1];
        $r = $w / $h;

        if ($w <= ($width + 1) && (($h <= ($height + 1)) || (!$height && !$width))) {
            if ($source != $destination) {
                self::OutputImage($source, RoxyFile::GetExtension(basename($source)), $destination, $quality);
            }
            return;
        }

        $newWidth = $width;
        $newHeight = floor($newWidth / $r);
        if (($height > 0 && $newHeight > $height) || !$width) {
            $newHeight = $height;
            $newWidth = intval($newHeight * $r);
        }

        $thumbImg = imagecreatetruecolor((int) $newWidth, (int) $newHeight);
        $img = self::GetImage($source);

        $thumbImg = self::SetAlpha($thumbImg, $source);

        imagecopyresampled($thumbImg, $img, 0, 0, 0, 0, (int) $newWidth, (int) $newHeight, $w, $h);

        self::OutputImage($thumbImg, RoxyFile::GetExtension(basename($source)), $destination, $quality);
    }

    public static function CropCenter(
        string $source,
        ?string $destination,
        int $width,
        int $height,
        int $quality = 90
    ): void {
        $tmp = (array) getimagesize($source);
        $w = $tmp[0];
        $h = $tmp[1];
        if (($w <= $width) && (!$height || ($h <= $height))) {
            self::OutputImage(self::GetImage($source), RoxyFile::GetExtension(basename($source)), $destination, $quality);
        }
        $ratio = $width / $height;
        $top = $left = 0;

        $cropWidth = floor($h * $ratio);
        $cropHeight = floor($cropWidth / $ratio);
        if ($cropWidth > $w) {
            $cropWidth = $w;
            $cropHeight = $w / $ratio;
        }
        if ($cropHeight > $h) {
            $cropHeight = $h;
            $cropWidth = $h * $ratio;
        }

        if ($cropWidth < $w) {
            $left = floor(($w - $cropWidth) / 2);
        }
        if ($cropHeight < $h) {
            $top = floor(($h - $cropHeight) / 2);
        }

        self::Crop($source, $destination, (int) $left, (int) $top, $cropWidth, $cropHeight, $width, $height, $quality);
    }

    public static function Crop(
        string $source,
        ?string $destination,
        int $x,
        int $y,
        int $cropWidth,
        int $cropHeight,
        int $width,
        int $height,
        int $quality = 90
    ): void {
        $thumbImg = imagecreatetruecolor($width, $height);
        $img = self::GetImage($source);

        $thumbImg = self::SetAlpha($thumbImg, $source);

        imagecopyresampled($thumbImg, $img, 0, 0, $x, $y, $width, $height, $cropWidth, $cropHeight);

        self::OutputImage($thumbImg, RoxyFile::GetExtension(basename($source)), $destination, $quality);
    }
}

$tmp = json_decode((string) file_get_contents(BASE_PATH . '/conf.json'), true);

if (!$tmp || !is_array($tmp)) {
    die('Error parsing configuration');
}

foreach ($tmp as $k => $v) {
    define((string) $k, $v);
}

$FilesRoot = fixPath(getFilesPath());
if (!is_dir($FilesRoot)) {
    @mkdir($FilesRoot, (int) octdec(DIRPERMISSIONS));
}