Merge branch 'master' of github.com:novashdima/ifm into novashdima-master

This commit is contained in:
Marco Dickert 2023-12-05 15:23:52 +01:00
commit f57ef6a469
17 changed files with 304 additions and 519 deletions

164
README.md
View File

@ -1,31 +1,34 @@
# IFM - improved file manager # IFM - improved file manager
## contents
- [about](#about) ## Contents
- [features](#features)
- [requirements](#requirements) * [about](#about)
- [installation](#installation) * [features](#features)
- [security information](#security-information) * [requirements](#requirements)
- [keybindings](#keybindings) * [installation](#installation)
- [configuration](#configuration) * [security information](#security-information)
- [docker](#docker) * [keybindings](#keybindings)
- [screenshots](#screenshots) * [configuration](#configuration)
- [issues](#issues) * [docker](#docker)
* [screenshots](#screenshots)
* [issues](#issues)
## About ## About
The IFM is a web-based filemanager, which comes as a single file solution using
HTML5, CSS3, JavaScript and PHP. You can test a [demo
here](https://ifmdemo.gitea.de/).
<a href="https://youtu.be/owJepSas19Y"><img src="https://img.youtube.com/vi/owJepSas19Y/hqdefault.jpg"></a> The IFM is a web-based filemanager, which comes as a single file solution using HTML5, CSS3, JavaScript and PHP. You can test a [demo here](https://ifmdemo.gitea.de/).
[![IFM](https://img.youtube.com/vi/owJepSas19Y/hqdefault.jpg)](https://youtu.be/owJepSas19Y)
The IFM uses the following resources: The IFM uses the following resources:
* [ACE Editor](https://ace.c9.io) * [ACE Editor](https://ace.c9.io)
* [Bootstrap v4](https://getbootstrap.com) * [Bootstrap v4](https://getbootstrap.com)
* custom icon set generated with [Fontello](http://fontello.com/) * custom icon set generated with [Fontello](http://fontello.com/)
* [jQuery](https://jquery.com) * [jQuery](https://jquery.com)
* [Mustache](https://mustache.github.io/) * [Mustache](https://mustache.github.io/)
## features ## Features
* create/edit files and directories * create/edit files and directories
* copy/move files and directories * copy/move files and directories
* download files and directories * download files and directories
@ -36,46 +39,40 @@ The IFM uses the following resources:
* simple authentication (LDAP via `ldap_bind` possible) * simple authentication (LDAP via `ldap_bind` possible)
## Requirements ## Requirements
* Client * Client
* HTML5 and CSS3 compatible browser + HTML5 and CSS3 compatible browser
* activated javascript + activated javascript
* Server * Server
* PHP >= 5.6 + PHP >= 5.6
* extensions + extensions
* bz2 - bz2
* curl (for remote upload) - curl (for remote upload)
* fileinfo - fileinfo
* json - json
* ldap (only if LDAP based authentication is used) - ldap (only if LDAP based authentication is used)
* mbstring - mbstring
* openssl (for remote uploads from https sources) - openssl (for remote uploads from https sources)
* phar - phar
* posix - posix
* zip - zip
* zlib - zlib
## Installation ## Installation
Just download the latest release of the IFM. You can find it
[here](https://github.com/misterunknown/ifm/releases/latest). You can choose
between the CDN version (dependencies like bootstrap, jquery etc. are loaded
via CDN) or the "simple" version, which bundles all these dependencies.
The minified versions (`*.min.php`) are zipped via gzip. These versions are not Just download the latest release of the IFM. You can find it [here](https://github.com/misterunknown/ifm/releases/latest). You can choose between the CDN version (dependencies like bootstrap, jquery etc. are loaded via CDN) or the "simple" version, which bundles all these dependencies.
recommended; if the filesize of the IFM is an issue for you, consider using the
CDN versions. The minified versions (`*.min.php`) are zipped via gzip. These versions are not recommended; if the filesize of the IFM is an issue for you, consider using the CDN versions.
## Security information ## Security information
The IFM is usually locked to it's own directory, so you are not able to go
above. You can change that by setting the `root_dir` in the scripts
[configuration](https://github.com/misterunknown/ifm/wiki/Configuration).
By default, it is not allowed to show or edit the `.htaccess` file. This is The IFM is usually locked to it's own directory, so you are not able to go above. You can change that by setting the `root_dir` in the scripts [configuration](https://github.com/misterunknown/ifm/wiki/Configuration).
because you can configure the IFM via environment variables. Thus if anyone has
the ability to edit the `.htaccess` file, he could overwrite the active By default, it is not allowed to show or edit the `.htaccess` file. This is because you can configure the IFM via environment variables. Thus if anyone has the ability to edit the `.htaccess` file, he could overwrite the active
configuration. [See configuration. [See also](https://github.com/misterunknown/ifm/wiki/Configuration).
also](https://github.com/misterunknown/ifm/wiki/Configuration).
## Keybindings
## Key bindings
* <kbd>e</kbd> - edit / extract current file * <kbd>e</kbd> - edit / extract current file
* <kbd>h</kbd><kbd>j</kbd><kbd>k</kbd><kbd>l</kbd> - vim-style navigation (alternative to arrow keys) * <kbd>h</kbd><kbd>j</kbd><kbd>k</kbd><kbd>l</kbd> - vim-style navigation (alternative to arrow keys)
* <kbd>g</kbd> - focus the path input field (i.e. "goto") * <kbd>g</kbd> - focus the path input field (i.e. "goto")
@ -95,55 +92,66 @@ also](https://github.com/misterunknown/ifm/wiki/Configuration).
* <kbd>Ctrl</kbd>-<kbd>Shift</kbd>-<kbd>f</kbd> - toggle fullscreen ace editor * <kbd>Ctrl</kbd>-<kbd>Shift</kbd>-<kbd>f</kbd> - toggle fullscreen ace editor
## Configuration ## Configuration
See [configuration](https://github.com/misterunknown/ifm/wiki/Configuration). See [configuration](https://github.com/misterunknown/ifm/wiki/Configuration).
### authentication
### Authentication
See [authentication](https://github.com/misterunknown/ifm/wiki/Authentication). See [authentication](https://github.com/misterunknown/ifm/wiki/Authentication).
## Docker ## Docker
The docker image is based on the official php docker images (alpine version) The docker image is based on the official php docker images (alpine version)
and exposes port 80. and exposes port 80.
### Quickstart ### Quickstart
Build the image with this command in the top source dir: Build the image with this command in the top source dir:
`docker build -t ifm .` ```bash
docker build -t ifm .
```
Afterwards you can start the docker container as follows: Afterwards you can start the docker container as follows:
`docker run --rm -d --name ifm -p 8080:80 -v /path/to/data:/var/www ifm:latest` ```bash
docker run --rm -d --name ifm -p 8080:80 -v /path/to/data:/var/www ifm:latest
### Specify user/group
By default IFM runs as user www-data (uid/gid 33). If you need to change that,
you can set the UID and GID with the following environment variables:
`docker run ... -e IFM_DOCKER_UID=1000 -e IFM_DOCKER_GID=100 ifm:latest`
### Other configuration
The script is located at `/usr/local/share/webapps/ifm/index.php`. By default
the `root_dir` is set to /var/www, so you can mount any directory at this
location. If you want to bind the corresponding host directory, you can do the
following:
`docker run --rm -i -p "8080:80" -v "/var/www:/var/www" ifm`
The scripts configuration can be changed by adjusting the corresponding
environment variables. For example:
```docker run --rm -i -p "8080:80" -v /var/www:/var/www \
-e IFM_AUTH=1 -e IFM_AUTH \
-e IFM_AUTH_SOURCE="admin:$2y$05$LPdE7u/5da/TCE8ZhqQ1o.acuV50HqB3OrHhNwxbXYeWmmZKdQxrC" \
ifm
``` ```
You can get a complete list of environment variables ### Specify user/group
[here](https://github.com/misterunknown/ifm/wiki/Configuration#configuration-options).
By default IFM runs as user www-data (uid/gid 33). If you need to change that, you can set the UID and GID with the following environment variables:
```bash
docker run ... -e IFM_DOCKER_UID=1000 -e IFM_DOCKER_GID=100 ifm:latest
```
### Other configuration
The script is located at `/usr/local/share/webapps/ifm/index.php`. By default the `root_dir` is set to /var/www, so you can mount any directory at this location. If you want to bind the corresponding host directory, you can do the following:
```bash
docker run --rm -i -p "8080:80" -v "/var/www:/var/www" ifm
```
The scripts configuration can be changed by adjusting the corresponding environment variables. For example:
```bash
docker run --rm -i -p "8080:80" -v /var/www:/var/www \
-e IFM_AUTH=1 -e IFM_AUTH \
-e IFM_AUTH_SOURCE="admin:$2y$05$LPdE7u/5da/TCE8ZhqQ1o.acuV50HqB3OrHhNwxbXYeWmmZKdQxrC" \
ifm
```
You can get a complete list of environment variables [here](https://github.com/misterunknown/ifm/wiki/Configuration#configuration-options).
## Screenshots
## screenshots
<a href="https://misterunknown.de/static/ifm_screenshot_desktop_filelist.png"><img src="https://misterunknown.de/static/ifm_screenshot_desktop_filelist.png" height="300px"></a> <a href="https://misterunknown.de/static/ifm_screenshot_desktop_filelist.png"><img src="https://misterunknown.de/static/ifm_screenshot_desktop_filelist.png" height="300px"></a>
<a href="https://misterunknown.de/static/ifm_screenshot_mobile_filelist.png"><img src="https://misterunknown.de/static/ifm_screenshot_mobile_filelist.png" height="300px"></a> <a href="https://misterunknown.de/static/ifm_screenshot_mobile_filelist.png"><img src="https://misterunknown.de/static/ifm_screenshot_mobile_filelist.png" height="300px"></a>
<a href="https://misterunknown.de/static/ifm_screenshot_desktop_remote_upload.png"><img src="https://misterunknown.de/static/ifm_screenshot_desktop_remote_upload.png" height="300px"></a> <a href="https://misterunknown.de/static/ifm_screenshot_desktop_remote_upload.png"><img src="https://misterunknown.de/static/ifm_screenshot_desktop_remote_upload.png" height="300px"></a>
<a href="https://misterunknown.de/static/ifm_screenshot_mobile_editfile.png"><img src="https://misterunknown.de/static/ifm_screenshot_mobile_editfile.png" height="300px"></a> <a href="https://misterunknown.de/static/ifm_screenshot_mobile_editfile.png"><img src="https://misterunknown.de/static/ifm_screenshot_mobile_editfile.png" height="300px"></a>
## issues ## Issues
If you happen to find an error or miss a feature, you can create an issue on
Github. If you happen to find an error or miss a feature, you can create an issue on Github.

View File

@ -26,16 +26,19 @@ $IFM_SRC_PHP = [
$options = getopt(null, ["language::", "languages::", "lang::", "cdn"]); $options = getopt(null, ["language::", "languages::", "lang::", "cdn"]);
// build CDN version? // build CDN version?
if (isset($options['cdn'])) if (isset($options['cdn'])) {
define("IFM_CDN", true); define("IFM_CDN", true);
else } else {
define("IFM_CDN", false); define("IFM_CDN", false);
}
// process languages // process languages
$langs = []; $langs = [];
foreach ($options as $key => $value) foreach ($options as $key => $value) {
if (substr($key, 0, 4) == "lang") if (substr($key, 0, 4) == "lang") {
$langs = array_merge($langs, explode(",", $value)); $langs = array_merge($langs, explode(",", $value));
}
}
$langs = array_unique($langs); $langs = array_unique($langs);
if (!empty($langs)) { if (!empty($langs)) {
@ -45,25 +48,28 @@ if (!empty($langs)) {
} }
// ensure english is available, as it gets merged with the other languages // ensure english is available, as it gets merged with the other languages
// in case of missing keys in other languages. // in case of missing keys in other languages.
if (!in_array("all", $langs) || !in_array("en", $langs)) if (!in_array("all", $langs) || !in_array("en", $langs)) {
array_push($langs, "en"); array_push($langs, "en");
}
if (in_array("all", $langs)) if (in_array("all", $langs))
$langs = array_map( $langs = array_map(
function($lang_file) {return pathinfo($lang_file)['filename']; }, function ($lang_file) { return pathinfo($lang_file)['filename']; },
glob("src/i18n/*.json") glob("src/i18n/*.json")
); );
$vars['languageincludes'] = ""; $vars['languageincludes'] = "";
foreach ($langs as $l) foreach ($langs as $l) {
if (file_exists("src/i18n/".$l.".json")) if (file_exists("src/i18n/".$l.".json")) {
$vars['languageincludes'] .= $vars['languageincludes'] .=
'$i18n["'.$l.'"] = <<<\'f00bar\'' . "\n" '$i18n["'.$l.'"] = <<<\'f00bar\'' . "\n"
. file_get_contents( "src/i18n/".$l.".json" ) . "\n" . file_get_contents( "src/i18n/".$l.".json" ) . "\n"
. 'f00bar;' . "\n" . 'f00bar;' . "\n"
. '$i18n["'.$l.'"] = json_decode( $i18n["'.$l.'"], true );' . "\n" ; . '$i18n["'.$l.'"] = json_decode( $i18n["'.$l.'"], true );' . "\n" ;
else } else {
print "WARNING: Language file src/i18n/".$l.".json not found.\n"; print "WARNING: Language file src/i18n/".$l.".json not found.\n";
}
}
// Concat PHP Files // Concat PHP Files
$compiled = ["<?php"]; $compiled = ["<?php"];
@ -81,11 +87,12 @@ $compiled = str_replace("###ASSETS_JS###", file_get_contents("src/assets".(IFM_C
// Process file includes // Process file includes
$includes = NULL; $includes = NULL;
preg_match_all("/\#\#\#file:([^\#]+)\#\#\#/", $compiled, $includes, PREG_SET_ORDER); preg_match_all("/\#\#\#file:([^\#]+)\#\#\#/", $compiled, $includes, PREG_SET_ORDER);
foreach ($includes as $file) foreach ($includes as $file) {
$compiled = str_replace($file[0], file_get_contents($file[1]), $compiled); $compiled = str_replace($file[0], file_get_contents($file[1]), $compiled);
}
// Process ace includes // Process ace includes
$includes = NULL; $includes = null;
$vars['ace_includes'] = ""; $vars['ace_includes'] = "";
preg_match_all("/\#\#\#acedir:([^\#]+)\#\#\#/", $compiled, $includes, PREG_SET_ORDER); preg_match_all("/\#\#\#acedir:([^\#]+)\#\#\#/", $compiled, $includes, PREG_SET_ORDER);
foreach ($includes as $dir) { foreach ($includes as $dir) {
@ -102,13 +109,15 @@ foreach ($includes as $dir) {
// Process variable includes // Process variable includes
$includes = NULL; $includes = NULL;
preg_match_all("/\#\#\#vars:([^\#]+)\#\#\#/", $compiled, $includes, PREG_SET_ORDER); preg_match_all("/\#\#\#vars:([^\#]+)\#\#\#/", $compiled, $includes, PREG_SET_ORDER);
foreach( $includes as $var ) foreach( $includes as $var ) {
$compiled = str_replace($var[0], $vars[$var[1]], $compiled); $compiled = str_replace($var[0], $vars[$var[1]], $compiled);
}
$compiled = str_replace('IFM_VERSION', IFM_VERSION, $compiled); $compiled = str_replace('IFM_VERSION', IFM_VERSION, $compiled);
if (!is_dir(IFM_RELEASE_DIR)) if (!is_dir(IFM_RELEASE_DIR)) {
mkdir(IFM_RELEASE_DIR); mkdir(IFM_RELEASE_DIR);
}
// build standalone ifm // build standalone ifm
file_put_contents(IFM_RELEASE_DIR . (IFM_CDN ? 'cdn.' : '') . IFM_STANDALONE, $compiled); file_put_contents(IFM_RELEASE_DIR . (IFM_CDN ? 'cdn.' : '') . IFM_STANDALONE, $compiled);
@ -123,7 +132,7 @@ $ifm->run();
// build compressed ifm // build compressed ifm
file_put_contents( file_put_contents(
IFM_RELEASE_DIR . (IFM_CDN ? 'cdn.' : '') . IFM_STANDALONE_GZ, IFM_RELEASE_DIR . (IFM_CDN ? 'cdn.' : '') . IFM_STANDALONE_GZ,
'<?php eval( gzdecode( file_get_contents( __FILE__, false, null, 85 ) ) ); exit(0); ?>' '<?php eval(gzdecode(file_get_contents(__FILE__, false, null, 85))); exit(0); ?>'
. gzencode(file_get_contents(IFM_RELEASE_DIR . (IFM_CDN ? 'cdn.' : '') . IFM_STANDALONE, false, null, 5)) . gzencode(file_get_contents(IFM_RELEASE_DIR . (IFM_CDN ? 'cdn.' : '') . IFM_STANDALONE, false, null, 5))
); );
// build lib // build lib

View File

@ -3,30 +3,30 @@ set -e
if [ ! -z $IFM_DOCKER_UID ]; then if [ ! -z $IFM_DOCKER_UID ]; then
# check if UID/GID are numeric # check if UID/GID are numeric
if ! echo "$IFM_DOCKER_UID$IFM_DOCKER_GID" | egrep "^[0-9]+$" >/dev/null 2>&1; then if ! echo "$IFM_DOCKER_UID$IFM_DOCKER_GID" | grep -E "^[0-9]+$" >/dev/null 2>&1; then
echo "FATAL: IFM_DOCKER_UID or IFM_DOCKER_GID are not numeric (UID: $IFM_DOCKER_UID, GID: $IFM_DOCKER_GID)" echo "FATAL: IFM_DOCKER_UID or IFM_DOCKER_GID are not numeric (UID: $IFM_DOCKER_UID, GID: $IFM_DOCKER_GID)"
exit 1 exit 1
fi fi
# get GID if not set # get GID if not set
if [ -z $IFM_DOCKER_GID ]; then if [ -z "$IFM_DOCKER_GID" ]; then
export IFM_DOCKER_GID=$IFM_DOCKER_UID export IFM_DOCKER_GID="$IFM_DOCKER_UID"
fi fi
# delete user if already exists # delete user if already exists
if getent passwd $IFM_DOCKER_UID >/dev/null 2>&1; then if getent passwd "$IFM_DOCKER_UID" >/dev/null 2>&1; then
deluser $(getent passwd $IFM_DOCKER_UID | sed "s/:.*//") deluser $(getent passwd "$IFM_DOCKER_UID" | sed "s/:.*//")
fi fi
# check if group already exists # check if group already exists
if ! getent group $IFM_DOCKER_GID >/dev/null 2>&1; then if ! getent group "$IFM_DOCKER_GID" >/dev/null 2>&1; then
addgroup -g $IFM_DOCKER_GID -S www-data addgroup -g "$IFM_DOCKER_GID" -S www-data
REAL_GROUP=www-data REAL_GROUP=www-data
else else
REAL_GROUP=$(getent group $IFM_DOCKER_GID | sed "s/:.*//") REAL_GROUP=$(getent group "$IFM_DOCKER_GID" | sed "s/:.*//")
fi fi
adduser -u $IFM_DOCKER_UID -SHDG $REAL_GROUP www-data adduser -u "$IFM_DOCKER_UID" -SHDG "$REAL_GROUP" www-data
else else
addgroup -g 33 -S www-data addgroup -g 33 -S www-data
adduser -SHD -u 33 -G www-data www-data adduser -SHD -u 33 -G www-data www-data

View File

@ -1,5 +1,5 @@
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/4.6.1/css/bootstrap.min.css" integrity="sha512-T584yQ/tdRR5QwOpfvDfVQUidzfgc2339Lc8uBDtcp/wYu80d7jwBgAxbyMh0a9YM9F8N3tdErpFI8iaGx6x5g==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/4.6.2/css/bootstrap.min.css" integrity="sha512-rt/SrQ4UNIaGfDyEXZtNcyWvQeOq0QLygHluFQcSjaGB04IxWhal71tKuzP6K8eYXYB6vJV4pHkXcmFGGQ1/0w==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/bs4/dt-1.11.3/r-2.2.9/datatables.min.css"/> <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/bs4/dt-1.13.1/r-2.4.0/datatables.min.css"/>
<style type="text/css"> <style type="text/css">
###file:src/includes/bootstrap-treeview.min.css### ###file:src/includes/bootstrap-treeview.min.css###
###file:src/includes/fontello-embedded.css### ###file:src/includes/fontello-embedded.css###

View File

@ -1,13 +1,13 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js" integrity="sha512-STof4xm1wgkfm7heWqFJVn58Hm3EtS31XFaagaa8VMReCXAkQnJZ+jEy8PCC/iT18dFy95WcExNHFTqLyp72eQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.1/umd/popper.min.js" integrity="sha512-ubuT8Z88WxezgSqf3RLuNi5lmjstiJcyezx34yIU2gAHonIi27Na7atqzUZCOoY4CExaoFumzOsFQ2Ch+I/HCw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.1/umd/popper.min.js" integrity="sha512-ubuT8Z88WxezgSqf3RLuNi5lmjstiJcyezx34yIU2gAHonIi27Na7atqzUZCOoY4CExaoFumzOsFQ2Ch+I/HCw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/4.6.1/js/bootstrap.min.js" integrity="sha512-UR25UO94eTnCVwjbXozyeVd6ZqpaAE9naiEUBK/A+QDbfSTQFhPGj5lOR6d8tsgbBk84Ggb5A3EkjsOgPRPcKA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/4.6.2/js/bootstrap.min.js" integrity="sha512-7rusk8kGPFynZWu26OKbTeI+QPoYchtxsmPeBqkHIEXJxeun4yJ4ISYe7C6sz9wdxeE1Gk3VxsIWgCZTc+vX3g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script type="text/javascript" src="https://cdn.datatables.net/v/bs4/dt-1.11.3/r-2.2.9/datatables.min.js"></script> <script type="text/javascript" src="https://cdn.datatables.net/v/bs4/dt-1.13.1/r-2.4.0/datatables.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script async src="https://cdnjs.cloudflare.com/ajax/libs/classnames/2.3.1/index.min.js" integrity="sha512-6Wf/IjsSjLaFTYco3pXM+49kC5M7jtbHzxMcdmYvwDskjv7cMcBPmJX2053aoQ+LRi8Po4ZsCtkNMf+NhXhNyQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script async src="https://cdnjs.cloudflare.com/ajax/libs/classnames/2.3.2/index.min.js" integrity="sha512-GqhSAi+WYQlHmNWiE4TQsVa7HVKctQMdgUMA+1RogjxOPdv9Kj59/no5BEvJgpvuMTYw2JRQu/szumfVXdowag==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/3.2.1/mustache.min.js" integrity="sha512-Qjrukx28QnvFWISw9y4wCB0kTB/ISnWXPz5/RME5o8OlZqllWygc1AB64dOBlngeTeStmYmNTNcM6kfEjUdnnQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script defer src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/3.2.1/mustache.min.js" integrity="sha512-Qjrukx28QnvFWISw9y4wCB0kTB/ISnWXPz5/RME5o8OlZqllWygc1AB64dOBlngeTeStmYmNTNcM6kfEjUdnnQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/mouse0270-bootstrap-notify/3.1.7/bootstrap-notify.min.js" integrity="sha512-BUaP7iu0aHqAISI8LphJT07Rv/MOiPI+mmq0h1rckNbzuKAW+UqmwEANqLPqanKNU331yApBM+40pZIkwkWAEQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script defer src="https://cdnjs.cloudflare.com/ajax/libs/mouse0270-bootstrap-notify/3.1.7/bootstrap-notify.min.js" integrity="sha512-BUaP7iu0aHqAISI8LphJT07Rv/MOiPI+mmq0h1rckNbzuKAW+UqmwEANqLPqanKNU331yApBM+40pZIkwkWAEQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js" integrity="sha512-uto9mlQzrs59VwILcLiRYeLKPPbS/bT71da/OEBYEwcdNUk8jYIy+D176RYoop1Da+f9mvkYrmj5MCLZWEtQuA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script defer src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.13.2/jquery-ui.min.js" integrity="sha512-57oZ/vW8ANMjR/KQ6Be9v/+/h6bq9/l3f0Oc7vn6qMqyhvPd1cvKBRWWpzu0QoneImqr2SkmO4MSqU+RpHom3Q==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.13/ace.min.js" integrity="sha512-jB1NOQkR0yLnWmEZQTUW4REqirbskxoYNltZE+8KzXqs9gHG5mrxLR5w3TwUn6AylXkhZZWTPP894xcX/X8Kbg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script defer src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.14.0/ace.min.js" integrity="sha512-s57ywpCtz+4PU992Bg1rDtr6+1z38gO2mS92agz2nqQcuMQ6IvgLWoQ2SFpImvg1rbgqBKeSEq0d9bo9NtBY0w==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script defer> <script defer>
###file:src/includes/bootstrap-treeview.min.js### ###file:src/includes/bootstrap-treeview.min.js###
###file:src/includes/BootstrapMenu.min.js### ###file:src/includes/BootstrapMenu.min.js###

View File

@ -6,53 +6,55 @@
class Htpasswd { class Htpasswd {
public $users = []; public $users = [];
public function __construct( $filename="" ) { public function __construct($filename="") {
if( $filename ) if ($filename) {
$this->load( $filename ); $this->load($filename);
}
} }
/** /**
* Load a new htpasswd file * Load a new htpasswd file
*/ */
public function load( $filename ) { public function load($filename) {
unset( $this->users ); unset($this->users);
if( file_exists( $filename ) && is_readable( $filename ) ) { if (file_exists($filename) && is_readable($filename)) {
$lines = file( $filename ); $lines = file( $filename );
foreach( $lines as $line ) { foreach ($lines as $line) {
list( $user, $pass ) = explode( ":", $line ); list($user, $pass) = explode(":", $line);
$this->users[$user] = trim( $pass ); $this->users[$user] = trim($pass);
} }
return true; return true;
} else
return false;
}
public function getUsers() {
return array_keys( $this->users );
}
public function userExist( $user ) {
return isset( $this->users[ $user ] );
}
public function verify( $user, $pass ) {
if( isset( $this->users[$user] ) ) {
return $this->verifyPassword( $pass, $this->users[$user] );
} else { } else {
return false; return false;
} }
} }
public function verifyPassword( $pass, $hash ) { public function getUsers() {
if( substr( $hash, 0, 4 ) == '$2y$' ) { return array_keys($this->users);
return password_verify( $pass, $hash ); }
} elseif( substr( $hash, 0, 6 ) == '$apr1$' ) {
public function userExist($user) {
return isset($this->users[$user]);
}
public function verify($user, $pass) {
if (isset($this->users[$user])) {
return $this->verifyPassword($pass, $this->users[$user]);
} else {
return false;
}
}
public function verifyPassword($pass, $hash) {
if (substr($hash, 0, 4) == '$2y$') {
return password_verify($pass, $hash);
} elseif (substr($hash, 0, 6) == '$apr1$') {
$apr1 = new APR1_MD5(); $apr1 = new APR1_MD5();
return $apr1->check( $pass, $hash ); return $apr1->check($pass, $hash);
} elseif( substr( $hash, 0, 5 ) == '{SHA}' ) { } elseif (substr($hash, 0, 5) == '{SHA}') {
return base64_encode( sha1( $pass, TRUE ) ) == substr( $hash, 5 ); return base64_encode(sha1($pass, true)) == substr($hash, 5);
} else { // assume CRYPT } else { // assume CRYPT
return crypt( $pass, $hash ) == $hash; return crypt($pass, $hash) == $hash;
} }
} }
} }
@ -64,64 +66,73 @@ class Htpasswd {
*/ */
class APR1_MD5 { class APR1_MD5 {
const BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; const BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
const APRMD5_ALPHABET = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; const APRMD5_ALPHABET = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
// Source/References for core algorithm: // Source/References for core algorithm:
// http://www.cryptologie.net/article/126/bruteforce-apr1-hashes/ // http://www.cryptologie.net/article/126/bruteforce-apr1-hashes/
// http://svn.apache.org/viewvc/apr/apr-util/branches/1.3.x/crypto/apr_md5.c?view=co // http://svn.apache.org/viewvc/apr/apr-util/branches/1.3.x/crypto/apr_md5.c?view=co
// http://www.php.net/manual/en/function.crypt.php#73619 // http://www.php.net/manual/en/function.crypt.php#73619
// http://httpd.apache.org/docs/2.2/misc/password_encryptions.html // http://httpd.apache.org/docs/2.2/misc/password_encryptions.html
// Wikipedia // Wikipedia
public static function hash($mdp, $salt = null) { public static function hash($mdp, $salt = null) {
if (is_null($salt)) if (is_null($salt)) {
$salt = self::salt(); $salt = self::salt();
$salt = substr($salt, 0, 8); }
$max = strlen($mdp); $salt = substr($salt, 0, 8);
$context = $mdp.'$apr1$'.$salt; $max = strlen($mdp);
$binary = pack('H32', md5($mdp.$salt.$mdp)); $context = $mdp.'$apr1$'.$salt;
for($i=$max; $i>0; $i-=16) $binary = pack('H32', md5($mdp.$salt.$mdp));
$context .= substr($binary, 0, min(16, $i)); for ($i=$max; $i>0; $i-=16) {
for($i=$max; $i>0; $i>>=1) $context .= substr($binary, 0, min(16, $i));
$context .= ($i & 1) ? chr(0) : $mdp[0]; }
$binary = pack('H32', md5($context)); for ($i=$max; $i>0; $i>>=1) {
for($i=0; $i<1000; $i++) { $context .= ($i & 1) ? chr(0) : $mdp[0];
$new = ($i & 1) ? $mdp : $binary; }
if($i % 3) $new .= $salt; $binary = pack('H32', md5($context));
if($i % 7) $new .= $mdp; for ($i=0; $i<1000; $i++) {
$new .= ($i & 1) ? $binary : $mdp; $new = ($i & 1) ? $mdp : $binary;
$binary = pack('H32', md5($new)); if ($i % 3) {
} $new .= $salt;
$hash = ''; }
for ($i = 0; $i < 5; $i++) { if ($i % 7) {
$k = $i+6; $new .= $mdp;
$j = $i+12; }
if($j == 16) $j = 5; $new .= ($i & 1) ? $binary : $mdp;
$hash = $binary[$i].$binary[$k].$binary[$j].$hash; $binary = pack('H32', md5($new));
} }
$hash = chr(0).chr(0).$binary[11].$hash; $hash = '';
$hash = strtr( for ($i = 0; $i < 5; $i++) {
strrev(substr(base64_encode($hash), 2)), $k = $i+6;
self::BASE64_ALPHABET, $j = $i+12;
self::APRMD5_ALPHABET if ($j == 16) {
); $j = 5;
return '$apr1$'.$salt.'$'.$hash; }
} $hash = $binary[$i].$binary[$k].$binary[$j].$hash;
}
$hash = chr(0).chr(0).$binary[11].$hash;
$hash = strtr(
strrev(substr(base64_encode($hash), 2)),
self::BASE64_ALPHABET,
self::APRMD5_ALPHABET
);
return '$apr1$'.$salt.'$'.$hash;
}
// 8 character salts are the best. Don't encourage anything but the best. // 8 character salts are the best. Don't encourage anything but the best.
public static function salt() { public static function salt() {
$alphabet = self::APRMD5_ALPHABET; $alphabet = self::APRMD5_ALPHABET;
$salt = ''; $salt = '';
for($i=0; $i<8; $i++) { for ($i=0; $i<8; $i++) {
$offset = hexdec(bin2hex(openssl_random_pseudo_bytes(1))) % 64; $offset = hexdec(bin2hex(openssl_random_pseudo_bytes(1))) % 64;
$salt .= $alphabet[$offset]; $salt .= $alphabet[$offset];
} }
return $salt; return $salt;
} }
public static function check($plain, $hash) { public static function check($plain, $hash) {
$parts = explode('$', $hash); $parts = explode('$', $hash);
return self::hash($plain, $parts[2]) === $hash; return self::hash($plain, $parts[2]) === $hash;
} }
} }

View File

@ -1399,7 +1399,7 @@ function IFM(params) {
* @returns {boolean} * @returns {boolean}
*/ */
this.inArray = function(needle, haystack) { this.inArray = function(needle, haystack) {
for( let i = 0; i < haystack.length; i++ ) for ( let i = 0; i < haystack.length; i++ )
if( haystack[i] == needle ) if( haystack[i] == needle )
return true; return true;
return false; return false;
@ -1575,7 +1575,7 @@ function IFM(params) {
this.generateGuid = function() { this.generateGuid = function() {
let result, i, j; let result, i, j;
result = ''; result = '';
for( j = 0; j < 20; j++ ) { for ( j = 0; j < 20; j++ ) {
i = Math.floor( Math.random() * 16 ).toString( 16 ).toUpperCase(); i = Math.floor( Math.random() * 16 ).toString( 16 ).toUpperCase();
result = result + i; result = result + i;
} }

View File

@ -24,8 +24,9 @@ class IFMArchive {
* Add a folder to an archive * Add a folder to an archive
*/ */
private static function addFolder(&$archive, $folder, $offset=0, $exclude_callback=null) { private static function addFolder(&$archive, $folder, $offset=0, $exclude_callback=null) {
if ($offset == 0) if ($offset == 0) {
$offset = strlen(dirname($folder)) + 1; $offset = strlen(dirname($folder)) + 1;
}
$archive->addEmptyDir(substr($folder, $offset)); $archive->addEmptyDir(substr($folder, $offset));
$handle = opendir($folder); $handle = opendir($folder);
while (false !== $f = readdir($handle)) { while (false !== $f = readdir($handle)) {
@ -33,13 +34,15 @@ class IFMArchive {
$filePath = $folder . '/' . $f; $filePath = $folder . '/' . $f;
if (file_exists($filePath) && is_readable($filePath)) { if (file_exists($filePath) && is_readable($filePath)) {
if (is_file($filePath)) { if (is_file($filePath)) {
if (!is_callable($exclude_callback) || $exclude_callback($f)) if (!is_callable($exclude_callback) || $exclude_callback($f)) {
$archive->addFile( $filePath, substr( $filePath, $offset ) ); $archive->addFile($filePath, substr($filePath, $offset));
}
} elseif (is_dir($filePath)) { } elseif (is_dir($filePath)) {
if (is_callable($exclude_callback)) if (is_callable($exclude_callback)) {
self::addFolder($archive, $filePath, $offset, $exclude_callback); self::addFolder($archive, $filePath, $offset, $exclude_callback);
else } else {
self::addFolder($archive, $filePath, $offset); self::addFolder($archive, $filePath, $offset);
}
} }
} }
} }
@ -54,18 +57,22 @@ class IFMArchive {
$a = new ZipArchive(); $a = new ZipArchive();
$a->open($archivename, ZIPARCHIVE::CREATE); $a->open($archivename, ZIPARCHIVE::CREATE);
if (!is_array($filenames)) if (!is_array($filenames)) {
$filenames = array($filenames); $filenames = array($filenames);
}
foreach ($filenames as $f) foreach ($filenames as $f)
if (is_dir($f)) if (is_dir($f)) {
if (is_callable($exclude_callback)) if (is_callable($exclude_callback)) {
self::addFolder( $a, $f, null, $exclude_callback ); self::addFolder($a, $f, null, $exclude_callback);
else } else {
self::addFolder( $a, $f ); self::addFolder($a, $f);
elseif (is_file($f)) }
if (!is_callable($exclude_callback) || $exclude_callback($f)) } elseif (is_file($f)) {
if (!is_callable($exclude_callback) || $exclude_callback($f)) {
$a->addFile($f, pathinfo($f, PATHINFO_BASENAME)); $a->addFile($f, pathinfo($f, PATHINFO_BASENAME));
}
}
try { try {
return $a->close(); return $a->close();
@ -78,16 +85,18 @@ class IFMArchive {
* Unzip a zip file * Unzip a zip file
*/ */
public static function extractZip($file, $destination="./") { public static function extractZip($file, $destination="./") {
if (!file_exists($file)) if (!file_exists($file)) {
return false; return false;
}
$zip = new ZipArchive; $zip = new ZipArchive;
$res = $zip->open($file); $res = $zip->open($file);
if ($res === true) { if ($res === true) {
$zip->extractTo($destination); $zip->extractTo($destination);
$zip->close(); $zip->close();
return true; return true;
} else } else {
return false; return false;
}
} }
/** /**
@ -98,14 +107,17 @@ class IFMArchive {
$a = new PharData($tmpf); $a = new PharData($tmpf);
try { try {
if (!is_array($filenames)) if (!is_array($filenames)) {
$filenames = array($filenames); $filenames = array($filenames);
}
foreach ($filenames as $f) foreach ($filenames as $f) {
if (is_dir($f)) if (is_dir($f)) {
self::addFolder($a, $f); self::addFolder($a, $f);
elseif (is_file($f)) } elseif (is_file($f)) {
$a->addFile($f, pathinfo($f, PATHINFO_BASENAME)); $a->addFile($f, pathinfo($f, PATHINFO_BASENAME));
}
}
switch ($format) { switch ($format) {
case "tar.gz": case "tar.gz":
$a->compress(Phar::GZ); $a->compress(Phar::GZ);
@ -127,8 +139,9 @@ class IFMArchive {
* Extracts a tar archive * Extracts a tar archive
*/ */
public static function extractTar($file, $destination="./") { public static function extractTar($file, $destination="./") {
if (!file_exists($file)) if (!file_exists($file)) {
return false; return false;
}
$tar = new PharData($file); $tar = new PharData($file);
try { try {
$tar->extractTo($destination, null, true); $tar->extractTo($destination, null, true);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
!function(){"use strict";var f={}.hasOwnProperty;function s(){for(var e=[],t=0;t<arguments.length;t++){var n=arguments[t];if(n){var r,o=typeof n;if("string"==o||"number"==o)e.push(n);else if(Array.isArray(n))!n.length||(r=s.apply(null,n))&&e.push(r);else if("object"==o)if(n.toString===Object.prototype.toString)for(var i in n)f.call(n,i)&&n[i]&&e.push(i);else e.push(n.toString())}}return e.join(" ")}"undefined"!=typeof module&&module.exports?(s.default=s,module.exports=s):"function"==typeof define&&"object"==typeof define.amd&&define.amd?define("classnames",[],function(){return s}):window.classNames=s}(); !function(){"use strict";var f={}.hasOwnProperty;function s(){for(var e=[],t=0;t<arguments.length;t++){var n=arguments[t];if(n){var o,r=typeof n;if("string"==r||"number"==r)e.push(n);else if(Array.isArray(n))!n.length||(o=s.apply(null,n))&&e.push(o);else if("object"==r)if(n.toString===Object.prototype.toString||n.toString.toString().includes("[native code]"))for(var i in n)f.call(n,i)&&n[i]&&e.push(i);else e.push(n.toString())}}return e.join(" ")}"undefined"!=typeof module&&module.exports?module.exports=s.default=s:"function"==typeof define&&"object"==typeof define.amd&&define.amd?define("classnames",[],function(){return s}):window.classNames=s}();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -220,7 +220,7 @@ f00bar;
case "getFolders": case "getFolders":
return $this->getFolders($_REQUEST); return $this->getFolders($_REQUEST);
} }
// checking working directory // checking working directory
if (!isset($_REQUEST["dir"]) || !$this->isPathValid($_REQUEST["dir"])) if (!isset($_REQUEST["dir"]) || !$this->isPathValid($_REQUEST["dir"]))
throw new IFMException($this->l("invalid_dir")); throw new IFMException($this->l("invalid_dir"));
@ -580,7 +580,7 @@ f00bar;
if (isset($d['filename']) && $this->isFilenameValid($d['filename'])) { if (isset($d['filename']) && $this->isFilenameValid($d['filename'])) {
if (isset($d['content'])) { if (isset($d['content'])) {
// work around magic quotes // work around magic quotes
if((function_exists("get_magic_quotes_gpc") && get_magic_quotes_gpc()) if ((function_exists("get_magic_quotes_gpc") && get_magic_quotes_gpc())
|| (ini_get('magic_quotes_sybase') && (strtolower(ini_get('magic_quotes_sybase'))!="off")) ) { || (ini_get('magic_quotes_sybase') && (strtolower(ini_get('magic_quotes_sybase'))!="off")) ) {
$content = stripslashes($d['content']); $content = stripslashes($d['content']);
} else { } else {
@ -1025,7 +1025,7 @@ f00bar;
if ($login_failed === true) if ($login_failed === true)
throw new IFMException("Authentication failed: Wrong credentials", true); throw new IFMException("Authentication failed: Wrong credentials", true);
else else
throw new IFMException("Not authenticated" , true); throw new IFMException("Not authenticated", true);
} }
} }
@ -1058,7 +1058,7 @@ f00bar;
} }
$u = $uuid . "=" . $user . "," . $basedn; $u = $uuid . "=" . $user . "," . $basedn;
if (!$ds = ldap_connect($ldap_server)) { if (!$ds = ldap_connect($ldap_server)) {
throw new IFMException("Could not reach the ldap server." , true); throw new IFMException("Could not reach the ldap server.", true);
//trigger_error("Could not reach the ldap server.", E_USER_ERROR); //trigger_error("Could not reach the ldap server.", E_USER_ERROR);
return false; return false;
} }
@ -1070,14 +1070,14 @@ f00bar;
if (ldap_count_entries($ds, ldap_search($ds, $u, $ufilter)) == 1) { if (ldap_count_entries($ds, ldap_search($ds, $u, $ufilter)) == 1) {
$authenticated = true; $authenticated = true;
} else { } else {
throw new IFMException("User not allowed." , true); throw new IFMException("User not allowed.", true);
//trigger_error("User not allowed.", E_USER_ERROR); //trigger_error("User not allowed.", E_USER_ERROR);
$authenticated = false; $authenticated = false;
} }
} else } else
$authenticated = true; $authenticated = true;
} else { } else {
throw new IFMException(ldap_error($ds) , true); throw new IFMException(ldap_error($ds), true);
//trigger_error(ldap_error($ds), E_USER_ERROR); //trigger_error(ldap_error($ds), E_USER_ERROR);
$authenticated = false; $authenticated = false;
} }
@ -1105,7 +1105,7 @@ f00bar;
private function isAbsolutePath($path) { private function isAbsolutePath($path) {
if ($path === null || $path === '') if ($path === null || $path === '')
return false; return false;
return $path[0] === DIRECTORY_SEPARATOR || preg_match('~^[A-Z]:(?![^/\\\\])~i',$path) > 0; return $path[0] === DIRECTORY_SEPARATOR || preg_match('~^[A-Z]:(?![^/\\\\])~i', $path) > 0;
} }
private function getRootDir() { private function getRootDir() {
@ -1247,7 +1247,7 @@ f00bar;
$ret = ""; $ret = "";
foreach ($parts as $part) foreach ($parts as $part)
if (trim($part) != "") if (trim($part) != "")
$ret .= (empty($ret) ? rtrim($part,"/") : trim($part, '/'))."/"; $ret .= (empty($ret) ? rtrim($part, "/") : trim($part, '/'))."/";
return rtrim($ret, "/"); return rtrim($ret, "/");
} }