├── .version ├── favicon.gif ├── favicon.ico ├── .gitattributes ├── robots.txt ├── languages └── index.php ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── HELP.yml │ └── BUG.yml └── CONTRIBUTING.md ├── default ├── fonts │ ├── cascadia-mono │ │ ├── cascadia-mono.woff2 │ │ └── cascadia-mono.css │ └── bootstrap-icons │ │ ├── bootstrap-icons.woff │ │ └── bootstrap-icons.woff2 ├── email │ ├── 2fa-code.txt │ ├── new-password.txt │ └── lost-password.txt ├── js │ ├── codemirror │ │ ├── README.md │ │ ├── rollup.config.mjs │ │ ├── package.json │ │ └── src │ │ │ ├── index.html │ │ │ └── languages │ │ │ └── config.js │ ├── jquery-ui │ │ ├── images │ │ │ ├── ui-icons_444444_256x240.png │ │ │ ├── ui-icons_555555_256x240.png │ │ │ ├── ui-icons_777620_256x240.png │ │ │ ├── ui-icons_777777_256x240.png │ │ │ ├── ui-icons_cc0000_256x240.png │ │ │ └── ui-icons_ffffff_256x240.png │ │ ├── LICENSE.txt │ │ └── package.json │ ├── components │ │ ├── tooltip.js │ │ ├── popover.js │ │ ├── modal.js │ │ ├── loading.js │ │ ├── confirmation.js │ │ ├── ajax.js │ │ └── console.js │ ├── navigation.js │ ├── ajax.js │ └── statistics.js ├── admin │ ├── modules │ │ ├── installing.php │ │ ├── empty.php │ │ ├── errors │ │ │ ├── empty.php │ │ │ └── list.php │ │ ├── repository │ │ │ └── empty.php │ │ └── info.php │ ├── users │ │ ├── empty.php │ │ ├── create.php │ │ ├── edit │ │ │ ├── settings.php │ │ │ └── account.php │ │ ├── list.php │ │ └── edit.php │ ├── themes │ │ └── empty.php │ └── themes.php ├── css │ ├── components │ │ ├── debug.css │ │ ├── themes.css │ │ ├── console.css │ │ ├── module-info.css │ │ └── file-tree.css │ ├── login.css │ └── global.css ├── functions.php ├── footer.php ├── assets │ └── systems │ │ └── ubuntu.svg ├── server │ ├── console.php │ ├── reboot.php │ └── packages.php ├── error │ ├── module_empty.php │ ├── module.php │ └── permissions.php ├── icons.json ├── modal.php └── module.php ├── classes ├── Modules │ ├── Installer.class.php │ ├── ModuleAuthor.class.php │ └── ModuleError.class.php ├── Installer │ ├── InstallerFactory.class.php │ └── Installer.class.php ├── System │ ├── HookParameters.class.php │ ├── CoreAdmin.class.php │ ├── Update.class.php │ └── Utils.class.php ├── Security │ └── Encryption.class.php ├── Network │ ├── Request.class.php │ ├── Route.class.php │ ├── Response.class.php │ ├── ResponseFactory.class.php │ ├── RequestFactory.class.php │ └── Router.class.php ├── Templating │ └── TemplateNavigation.class.php ├── Hardware │ ├── NetworkState.enum.php │ ├── NetworkFamily.enum.php │ ├── NetworkAddress.class.php │ ├── PhysicalDrives.class.php │ ├── Memory.class.php │ ├── NetworkFlag.enum.php │ ├── NetworkInterface.class.php │ └── NetworkInterfaces.class.php ├── Accounting │ ├── Session.class.php │ └── Auth.class.php ├── UI │ ├── Button.class.php │ ├── Modal.class.php │ └── Icon.class.php ├── Storage │ ├── Database.class.php │ └── PropertiesCache.class.php └── Localization │ └── I18N.class.php ├── libraries ├── Sepia │ └── PoParser │ │ ├── Exception │ │ └── ParseException.php │ │ ├── Catalog │ │ ├── Catalog.php │ │ ├── Header.php │ │ ├── EntryFactory.php │ │ └── CatalogArray.php │ │ └── SourceHandler │ │ ├── SourceHandler.php │ │ ├── StringSource.php │ │ └── FileSystem.php ├── skoerfgen │ ├── ACMECert.php │ ├── alpn_responder.js │ ├── composer.json │ ├── LICENSE.md │ └── src │ │ └── ACME_Exception.php ├── Markdown │ ├── composer.json │ └── LICENSE.txt ├── Parsedown │ ├── composer.json │ └── LICENSE.txt └── PHPMailer │ ├── Exception.php │ └── OAuth.php ├── .gitignore ├── handler ├── server │ ├── services.modal.php │ ├── settings.php │ ├── reboot.php │ ├── network.php │ ├── logs.php │ └── server.php ├── settings.php └── settings │ ├── default.php │ └── security.php ├── index.php ├── LICENSE └── .htaccess /.version: -------------------------------------------------------------------------------- 1 | 1.0.4 -------------------------------------------------------------------------------- /favicon.gif: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /languages/index.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: fruithost 2 | patreon: fruithost 3 | custom: ['https://paypal.me/debitdirect'] 4 | -------------------------------------------------------------------------------- /default/fonts/cascadia-mono/cascadia-mono.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fruithost/Panel/HEAD/default/fonts/cascadia-mono/cascadia-mono.woff2 -------------------------------------------------------------------------------- /default/email/2fa-code.txt: -------------------------------------------------------------------------------- 1 | Hey $USERNAME, 2 | 3 | here is your 2FA Code for your login request: 4 | $CODE 5 | 6 | Thanks, 7 | 8 | The fruithost Team -------------------------------------------------------------------------------- /default/fonts/bootstrap-icons/bootstrap-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fruithost/Panel/HEAD/default/fonts/bootstrap-icons/bootstrap-icons.woff -------------------------------------------------------------------------------- /default/fonts/bootstrap-icons/bootstrap-icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fruithost/Panel/HEAD/default/fonts/bootstrap-icons/bootstrap-icons.woff2 -------------------------------------------------------------------------------- /default/js/codemirror/README.md: -------------------------------------------------------------------------------- 1 | # How to Build? 2 | 3 | > npm install 4 | > npm start 5 | 6 | The builded editor will be bundled on `build/bundle.js`. -------------------------------------------------------------------------------- /classes/Modules/Installer.class.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /default/js/jquery-ui/images/ui-icons_444444_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fruithost/Panel/HEAD/default/js/jquery-ui/images/ui-icons_444444_256x240.png -------------------------------------------------------------------------------- /default/js/jquery-ui/images/ui-icons_555555_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fruithost/Panel/HEAD/default/js/jquery-ui/images/ui-icons_555555_256x240.png -------------------------------------------------------------------------------- /default/js/jquery-ui/images/ui-icons_777620_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fruithost/Panel/HEAD/default/js/jquery-ui/images/ui-icons_777620_256x240.png -------------------------------------------------------------------------------- /default/js/jquery-ui/images/ui-icons_777777_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fruithost/Panel/HEAD/default/js/jquery-ui/images/ui-icons_777777_256x240.png -------------------------------------------------------------------------------- /default/js/jquery-ui/images/ui-icons_cc0000_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fruithost/Panel/HEAD/default/js/jquery-ui/images/ui-icons_cc0000_256x240.png -------------------------------------------------------------------------------- /default/js/jquery-ui/images/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fruithost/Panel/HEAD/default/js/jquery-ui/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /libraries/Sepia/PoParser/Exception/ParseException.php: -------------------------------------------------------------------------------- 1 | 10 |
11 | 	
14 | 
-------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /handler/settings.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /default/admin/modules/installing.php: -------------------------------------------------------------------------------- 1 | 10 |
11 |
12 | Loading... 13 |
14 |
15 | 16 | -------------------------------------------------------------------------------- /default/css/components/debug.css: -------------------------------------------------------------------------------- 1 | /* 2 | * fruithost | OpenSource Hosting 3 | * 4 | * @author Adrian Preuß 5 | * @version 1.0.0 6 | * @license MIT 7 | */ 8 | 9 | .xdebug-error, .xdebug-var-dump { 10 | z-index: 9999999999; 11 | position: relative; 12 | overflow: visible !important; 13 | background: #FEFEFE; 14 | color: #000000; 15 | } 16 | 17 | [data-bs-theme="dark"] .xdebug-error, [data-bs-theme="dark"] .xdebug-var-dump { 18 | background: #444444; 19 | } 20 | -------------------------------------------------------------------------------- /default/js/codemirror/rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import {nodeResolve} from "@rollup/plugin-node-resolve"; 2 | import terser from '@rollup/plugin-terser'; 3 | 4 | export default { 5 | input: './src/main.js', 6 | output: [{ 7 | file: './build/bundle.src.js', 8 | format: 'iife' 9 | }, { 10 | file: './build/bundle.min.js', 11 | sourcemap: true, 12 | format: 'iife', 13 | plugins: [ 14 | terser() 15 | ] 16 | }], 17 | plugins: [ 18 | nodeResolve() 19 | ], 20 | }; -------------------------------------------------------------------------------- /default/js/components/tooltip.js: -------------------------------------------------------------------------------- 1 | /** 2 | * fruithost | OpenSource Hosting 3 | * 4 | * @author Adrian Preuß 5 | * @version 1.0.0 6 | * @license MIT 7 | */ 8 | 'use strict'; 9 | 10 | class Tooltip { 11 | constructor() { 12 | document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach((element) => { 13 | new bootstrap.Tooltip(element) 14 | }); 15 | } 16 | } 17 | 18 | window.addEventListener('DOMContentLoaded', () => { 19 | window.Tooltip = new Tooltip(); 20 | }); -------------------------------------------------------------------------------- /default/admin/users/empty.php: -------------------------------------------------------------------------------- 1 | 13 |
14 | 15 |

16 |

17 |
-------------------------------------------------------------------------------- /default/admin/modules/empty.php: -------------------------------------------------------------------------------- 1 | 13 |
14 | 15 |

16 |

17 |
-------------------------------------------------------------------------------- /default/admin/themes/empty.php: -------------------------------------------------------------------------------- 1 | 13 |
14 | 15 |

16 |

17 |
-------------------------------------------------------------------------------- /default/functions.php: -------------------------------------------------------------------------------- 1 | getFiles()->addStylesheet('login', $this->url('css/login.css'), '2.0.0', [ 'bootstrap' ]); 7 | } else { 8 | $this->getFiles()->addStylesheet('style', $this->url('css/style.css'), '2.0.0', [ 'bootstrap' ]); 9 | $this->getFiles()->addJavascript('navigation', $this->url('js/navigation.js'), '2.0.0', [ 'bootstrap' ], TemplateFiles::FOOTER); 10 | } 11 | ?> -------------------------------------------------------------------------------- /default/admin/modules/errors/empty.php: -------------------------------------------------------------------------------- 1 | 13 |
14 | 15 |

16 |

17 |
-------------------------------------------------------------------------------- /default/footer.php: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | 18 | 19 | foot(); 23 | ?> 24 | 25 | -------------------------------------------------------------------------------- /default/js/components/popover.js: -------------------------------------------------------------------------------- 1 | /** 2 | * fruithost | OpenSource Hosting 3 | * 4 | * @author Adrian Preuß 5 | * @version 1.0.0 6 | * @license MIT 7 | */ 8 | 'use strict'; 9 | 10 | class Popover { 11 | constructor() { 12 | document.querySelectorAll('[data-bs-toggle="popover"], [data-bs-toggle="hover"]').forEach((element) => { 13 | new bootstrap.Popover(element, { 14 | trigger: 'hover', 15 | html: true 16 | }); 17 | }); 18 | } 19 | } 20 | 21 | window.addEventListener('DOMContentLoaded', () => { 22 | window.Popover = new Popover(); 23 | }); -------------------------------------------------------------------------------- /classes/Installer/InstallerFactory.class.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /classes/System/HookParameters.class.php: -------------------------------------------------------------------------------- 1 | args = func_get_args(); 17 | } 18 | 19 | public function get(?int $position = null) : mixed { 20 | if(!empty($position)) { 21 | return $this->args[$position]; 22 | } 23 | 24 | return $this->args; 25 | } 26 | } 27 | ?> -------------------------------------------------------------------------------- /handler/settings/default.php: -------------------------------------------------------------------------------- 1 | getCore()->getHooks()->runAction('SAVE_ACCOUNT_SETTINGS_GLOBAL', $_POST); 18 | $template->assign('success', I18N::get('Your settings has been updated.')); 19 | I18N::reload(); 20 | ?> -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/HELP.yml: -------------------------------------------------------------------------------- 1 | name: ❔ Needing Help 2 | description: You have regular questions? Then ask them here... 3 | title: "[Help] " 4 | labels: ["help"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for taking the time to fill out this bug report! 10 | Please **dont share** personal contact data (for sample `E-Mail` or `Discord`-Name), we never contact you here because we dont offer premium-support. 11 | - type: textarea 12 | id: what-happened 13 | attributes: 14 | label: Question 15 | description: Tell us what you need help with! 16 | placeholder: Your question 17 | validations: 18 | required: true 19 | -------------------------------------------------------------------------------- /default/css/login.css: -------------------------------------------------------------------------------- 1 | /* 2 | * fruithost | OpenSource Hosting 3 | * 4 | * @author Adrian Preuß 5 | * @version 1.0.0 6 | * @license MIT 7 | */ 8 | 9 | .form-signin { 10 | max-width: 330px; 11 | padding: 1rem; 12 | } 13 | 14 | .form-signin .form-floating:focus-within { 15 | z-index: 2; 16 | } 17 | 18 | .form-signin input[name="username"], 19 | .form-signin input[name="password_new"] { 20 | margin-bottom: -1px; 21 | border-bottom-right-radius: 0; 22 | border-bottom-left-radius: 0; 23 | } 24 | 25 | .form-signin input[name="password"], 26 | .form-signin input[name="password_repeated"] { 27 | margin-bottom: 10px; 28 | border-top-left-radius: 0; 29 | border-top-right-radius: 0; 30 | } -------------------------------------------------------------------------------- /default/admin/modules/repository/empty.php: -------------------------------------------------------------------------------- 1 | 13 |
14 | 15 |

16 |

17 | 18 |
-------------------------------------------------------------------------------- /classes/Security/Encryption.class.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /default/js/components/modal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * fruithost | OpenSource Hosting 3 | * 4 | * @author Adrian Preuß 5 | * @version 1.0.0 6 | * @license MIT 7 | */ 8 | 'use strict'; 9 | 10 | class Modal { 11 | constructor() { 12 | if(typeof(window.showing) === 'undefined') { 13 | console.warn('[WARNING]', 'window.showing', 'is Empty!'); 14 | return; 15 | } 16 | 17 | window.showing.forEach((modal) => { 18 | let instance = new bootstrap.Modal('#' + modal, {}); 19 | 20 | if(instance) { 21 | instance.show(); 22 | } 23 | }); 24 | } 25 | } 26 | 27 | window.addEventListener('DOMContentLoaded', () => { 28 | window.Modal = new Modal(); 29 | }); -------------------------------------------------------------------------------- /classes/Network/Request.class.php: -------------------------------------------------------------------------------- 1 | init(); 15 | } 16 | 17 | public static function has(string $name) : bool { 18 | return RequestFactory::getInstance()->has($name); 19 | } 20 | 21 | public static function get(string $name) : mixed { 22 | return RequestFactory::getInstance()->get($name); 23 | } 24 | 25 | public static function url() : string { 26 | return RequestFactory::getInstance()->url(); 27 | } 28 | } 29 | ?> -------------------------------------------------------------------------------- /classes/Network/Route.class.php: -------------------------------------------------------------------------------- 1 | path; 18 | } 19 | 20 | public function setPath(string $path) : void { 21 | $this->path = $path; 22 | } 23 | 24 | public function getCallback() : ?\Closure { 25 | return $this->callback; 26 | } 27 | 28 | public function setCallback(?\Closure $callback) : void { 29 | $this->callback = $callback; 30 | } 31 | } 32 | ?> -------------------------------------------------------------------------------- /libraries/skoerfgen/alpn_responder.js: -------------------------------------------------------------------------------- 1 | /* ACMECert Example ALPN Responder (only needed when using tls-alpn-01 challenge) */ 2 | var tls=require('tls'); 3 | var fs=require('fs'); 4 | 5 | var server=tls.createServer({ 6 | key:fs.readFileSync(process.argv[2]), 7 | cert:fs.readFileSync(process.argv[3]), 8 | ALPNProtocols:['acme-tls/1'] 9 | }).on('listening',function(){ 10 | console.log('ACMECert Example ALPN Responder - Ready'); 11 | }).on('secureConnection',function(socket){ 12 | socket.on('error',function(){ 13 | }); 14 | }).listen(443); 15 | 16 | process.stdin.resume(); 17 | process.stdin.on('end',function(){ 18 | server.close(function () { 19 | console.log('ACMECert Example ALPN Responder - Terminating'); 20 | process.exit(0); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /default/css/components/themes.css: -------------------------------------------------------------------------------- 1 | /* 2 | * fruithost | OpenSource Hosting 3 | * 4 | * @author Adrian Preuß 5 | * @version 1.0.0 6 | * @license MIT 7 | */ 8 | 9 | .theme-switch { 10 | z-index: 99999; 11 | border-radius: 35%; 12 | color: var(--bs-emphasis-color); 13 | background-color: var(--bs-secondary-bg-subtle); 14 | } 15 | 16 | .theme-switch .bi { 17 | vertical-align: -.125em; 18 | fill: currentColor; 19 | } 20 | 21 | .theme-switch .btn-bd-primary:active, 22 | .theme-switch .btn-bd-primary:focus { 23 | --bs-btn-active-border-color: transparent; 24 | } 25 | 26 | .theme-switch .bd-mode-toggle { 27 | z-index: 1500; 28 | } 29 | 30 | .theme-switch .bd-mode-toggle .dropdown-menu .active .bi { 31 | display: block !important; 32 | } -------------------------------------------------------------------------------- /libraries/skoerfgen/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "skoerfgen/acmecert", 3 | "version": "3.2.2", 4 | "description": "PHP client library for Let's Encrypt and other ACME v2 - RFC 8555 compatible Certificate Authorities", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Stefan Körfgen", 9 | "homepage": "https://github.com/skoerfgen" 10 | } 11 | ], 12 | "support": { 13 | "issues": "https://github.com/skoerfgen/ACMECert/issues", 14 | "source": "https://github.com/skoerfgen/ACMECert" 15 | }, 16 | "require": { 17 | "php": ">=5.6.0", 18 | "ext-openssl": "*" 19 | }, 20 | "suggest": { 21 | "ext-curl": "Optional for better http performance" 22 | }, 23 | "autoload": { 24 | "psr-4": { 25 | "skoerfgen\\ACMECert\\": "src" 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /classes/Network/Response.class.php: -------------------------------------------------------------------------------- 1 | addHeader($name, $value); 15 | } 16 | 17 | public static function header() : void { 18 | ResponseFactory::getInstance()->header(); 19 | } 20 | 21 | public static function setContentType(string $type) : void { 22 | ResponseFactory::getInstance()->setContentType($type); 23 | } 24 | 25 | public static function redirect(string $url) : void { 26 | ResponseFactory::getInstance()->redirect($url); 27 | } 28 | } 29 | ?> -------------------------------------------------------------------------------- /default/assets/systems/ubuntu.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /default/js/codemirror/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "editor", 3 | "version": "1.0.0", 4 | "main": "src/main.js", 5 | "type": "module", 6 | "scripts": { 7 | "start": "rollup --config rollup.config.mjs" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "MIT", 12 | "description": "", 13 | "devDependencies": { 14 | "@rollup/plugin-node-resolve": "^15.2.3", 15 | "@rollup/plugin-terser": "^0.4.4", 16 | "rollup": "^4.21.0" 17 | }, 18 | "dependencies": { 19 | "@codemirror/lang-html": "^6.4.9", 20 | "@codemirror/lang-javascript": "^6.2.2", 21 | "@codemirror/lang-json": "^6.0.1", 22 | "@codemirror/language": "^6.10.2", 23 | "@codemirror/legacy-modes": "^6.4.1", 24 | "@codemirror/state": "^6.4.1", 25 | "@codemirror/view": "^6.33.0", 26 | "@lezer/highlight": "^1.2.1", 27 | "codemirror": "^6.0.1" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /libraries/Sepia/PoParser/Catalog/Catalog.php: -------------------------------------------------------------------------------- 1 | =5.3.0", 17 | "ext-mbstring": "*" 18 | }, 19 | "require-dev": { 20 | "phpunit/phpunit": "^4.8.35" 21 | }, 22 | "autoload": { 23 | "psr-0": {"Parsedown": ""} 24 | }, 25 | "autoload-dev": { 26 | "psr-0": { 27 | "TestParsedown": "test/", 28 | "ParsedownTest": "test/", 29 | "CommonMarkTest": "test/", 30 | "CommonMarkTestWeak": "test/" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /libraries/Parsedown/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "erusev/parsedown", 3 | "description": "Parser for Markdown.", 4 | "keywords": ["markdown", "parser"], 5 | "homepage": "http://parsedown.org", 6 | "type": "library", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Emanuil Rusev", 11 | "email": "hello@erusev.com", 12 | "homepage": "http://erusev.com" 13 | } 14 | ], 15 | "require": { 16 | "php": ">=5.3.0", 17 | "ext-mbstring": "*" 18 | }, 19 | "require-dev": { 20 | "phpunit/phpunit": "^4.8.35" 21 | }, 22 | "autoload": { 23 | "psr-0": {"Parsedown": ""} 24 | }, 25 | "autoload-dev": { 26 | "psr-0": { 27 | "TestParsedown": "test/", 28 | "ParsedownTest": "test/", 29 | "CommonMarkTest": "test/", 30 | "CommonMarkTestWeak": "test/" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /classes/Modules/ModuleAuthor.class.php: -------------------------------------------------------------------------------- 1 | name)) { 19 | $this->name = $object->name; 20 | } 21 | 22 | if(!empty($object->email)) { 23 | $this->email = $object->email; 24 | } 25 | 26 | if(!empty($object->url)) { 27 | $this->url = $object->url; 28 | } 29 | } 30 | 31 | public function getName() : ?string { 32 | return $this->name; 33 | } 34 | 35 | public function getMail() : ?string { 36 | return $this->email; 37 | } 38 | 39 | public function getWebsite() : ?string { 40 | return $this->url; 41 | } 42 | } 43 | ?> -------------------------------------------------------------------------------- /default/css/components/console.css: -------------------------------------------------------------------------------- 1 | /* 2 | * fruithost | OpenSource Hosting 3 | * 4 | * @author Adrian Preuß 5 | * @version 1.0.0 6 | * @license MIT 7 | */ 8 | 9 | div.terminal { 10 | position: relative; 11 | margin: 0 -1.5rem !important; 12 | width: calc(100% + 3rem); 13 | background: #282c34; 14 | color: #dcdfe4; 15 | box-shadow: inset 0px 2px 10px rgba(0, 0, 0, .25); 16 | } 17 | 18 | div.terminal div.output { 19 | height: calc(100% - 35px); 20 | overflow: auto; 21 | padding: 15px; 22 | line-height: 14px; 23 | font-size: 12px; 24 | font-family: 'Cascadia Mono'; 25 | } 26 | 27 | div.terminal div.output span[data-color="0;32"] { 28 | color: #9bc67c; 29 | } 30 | 31 | div.terminal div.output span[data-color="38;5;202"] { 32 | color: #e5c07b; 33 | } 34 | 35 | div.terminal input[name="command"] { 36 | height: 35px; 37 | width: 100%; 38 | background: rgba(255, 255, 255, 0.2); 39 | border: 0; 40 | padding: 10px; 41 | color: #FFFFFF; 42 | } -------------------------------------------------------------------------------- /default/css/components/module-info.css: -------------------------------------------------------------------------------- 1 | /* 2 | * fruithost | OpenSource Hosting 3 | * 4 | * @author Adrian Preuß 5 | * @version 1.0.0 6 | * @license MIT 7 | */ 8 | div#module_info .module-icon { 9 | font-size: 100px; 10 | } 11 | 12 | div#module_info .carousel-caption { 13 | right: 0; 14 | bottom: 0; 15 | left: 0; 16 | color: #fff !important; 17 | background: rgba(0, 0, 0, 0.5); 18 | } 19 | 20 | div#module_info div.markdown { 21 | background: var(--bs-modal-footer-border-color); 22 | padding: 20px; 23 | font-size: 16px; 24 | } 25 | 26 | div#module_info div.markdown h1 { 27 | font-size: 1.4rem; 28 | } 29 | 30 | div#module_info div.markdown h2 { 31 | font-size: 1.3rem; 32 | } 33 | 34 | div#module_info div.markdown h3 { 35 | font-size: 1.2rem; 36 | } 37 | 38 | div#module_info div.markdown h4 { 39 | font-size: 1.1rem; 40 | } 41 | 42 | div#module_info div.markdown h5 { 43 | font-size: 1.0rem; 44 | } 45 | 46 | div#module_info div.markdown h6 { 47 | font-size: 0.9rem; 48 | } -------------------------------------------------------------------------------- /default/server/console.php: -------------------------------------------------------------------------------- 1 | header(); 14 | if(!Auth::hasPermission('SERVER::CONSOLE')) { 15 | ?> 16 | 20 | footer(); 22 | exit(); 23 | } 24 | ?> 25 |
26 |
27 | 28 |
29 | 30 | footer(); 32 | ?> -------------------------------------------------------------------------------- /default/js/components/loading.js: -------------------------------------------------------------------------------- 1 | /** 2 | * fruithost | OpenSource Hosting 3 | * 4 | * @author Adrian Preuß 5 | * @version 1.0.0 6 | * @license MIT 7 | */ 8 | 9 | (() => { 10 | window.Loading = function Loading(element) { 11 | element.addEventListener('click', function (event) { 12 | let element = event.target; 13 | 14 | element.innerHTML = ''; 15 | 16 | let spinner = document.createElement('span'); 17 | spinner.classList.add('spinner-border'); 18 | spinner.classList.add('spinner-border-sm'); 19 | element.append(spinner); 20 | 21 | let text = document.createElement('span'); 22 | text.innerText = ' ' + element.dataset.loading; 23 | element.append(text); 24 | 25 | let dots = document.createElement('span'); 26 | dots.dataset.dots = 'true'; 27 | element.append(dots); 28 | }, false); 29 | }; 30 | 31 | [].map.call(document.querySelectorAll('[data-loading]'), Loading); 32 | })(); -------------------------------------------------------------------------------- /default/css/components/file-tree.css: -------------------------------------------------------------------------------- 1 | /* 2 | * fruithost | OpenSource Hosting 3 | * 4 | * @author Adrian Preuß 5 | * @version 1.0.0 6 | * @license MIT 7 | */ 8 | .tree { 9 | position: relative; 10 | display: -ms-flexbox; 11 | display: flex; 12 | -ms-flex-direction: column; 13 | flex-direction: column; 14 | min-width: 0; 15 | word-wrap: break-word; 16 | padding: 0; 17 | overflow: hidden; 18 | } 19 | 20 | .tree .list-group { 21 | margin-bottom: 0; 22 | } 23 | 24 | .tree .list-group-item { 25 | border-radius: 0; 26 | border-width: 1px 0 0 0; 27 | padding-top: 0.5rem; 28 | padding-bottom: 0.5rem; 29 | cursor: pointer; 30 | } 31 | 32 | .tree [data-type="directory"]::before { 33 | content: "\F3D9"; 34 | position: relative; 35 | display: inline-block; 36 | font-family: 'bootstrap-icons'; 37 | font-size: 20px; 38 | color: #f3c36b; 39 | } 40 | 41 | .tree .list-group-item:hover { 42 | background-color: #dee2e6; 43 | } 44 | 45 | .tree > .list-group-item:first-child { 46 | border-top-width: 0; 47 | } -------------------------------------------------------------------------------- /classes/Templating/TemplateNavigation.class.php: -------------------------------------------------------------------------------- 1 | core = $core; 20 | } 21 | 22 | public function addCategory(string $name, string $label) : void { 23 | $category = new TemplateNavigationCategory($this, $name, $label); 24 | $this->entries[$category->getID()] = $category; 25 | } 26 | 27 | public function getCore() : Core { 28 | return $this->core; 29 | } 30 | 31 | public function getCategory(string $name) : TemplateNavigationCategory { 32 | return $this->entries[$name]; 33 | } 34 | 35 | public function isEmpty() : bool { 36 | return (count($this->entries) === 0); 37 | } 38 | 39 | public function getEntries() : array { 40 | return $this->entries; 41 | } 42 | } 43 | ?> -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 fruithost | OpenSource Hosting 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /libraries/skoerfgen/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Stefan Körfgen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /libraries/Markdown/LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2018 Emanuil Rusev, erusev.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /libraries/Parsedown/LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2018 Emanuil Rusev, erusev.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /handler/server/settings.php: -------------------------------------------------------------------------------- 1 | assign('error', I18N::get('You have no permissions for this action!')); 18 | return; 19 | } 20 | 21 | $template->getCore()->setSettings('PROJECT_NAME', $_POST['project_name']); 22 | $template->getCore()->setSettings('PROJECT_COPYRIGHT', isset($_POST['project_copyright']) && $_POST['project_copyright'] == 'true'); 23 | $template->getCore()->setSettings('LANGUAGE', $_POST['language']); 24 | $template->getCore()->setSettings('TIME_FORMAT', $_POST['time_format']); 25 | $template->getCore()->setSettings('TIME_ZONE', $_POST['time_zone']); 26 | $template->getCore()->getHooks()->runAction('SAVE_SERVER_SETTINGS', $_POST); 27 | $this->assign('success', I18N::get('The system settings has been saved.')); 28 | break; 29 | } 30 | } 31 | ?> -------------------------------------------------------------------------------- /classes/Hardware/NetworkState.enum.php: -------------------------------------------------------------------------------- 1 | value){ 20 | return $status; 21 | } 22 | } 23 | 24 | throw new \ValueError("$name is not a valid backing value for enum " . self::class); 25 | } 26 | 27 | public static function for(NetworkState $name) : string { 28 | foreach(self::cases() as $status) { 29 | if($status->name === $name->name) { 30 | return $status->name; 31 | } 32 | } 33 | 34 | throw new \ValueError("$name is not a valid backing value for enum " . self::class); 35 | } 36 | 37 | public static function tryFromName(string $name) : NetworkState | null { 38 | try { 39 | return self::fromName($name); 40 | } catch (\ValueError $error) { 41 | return null; 42 | } 43 | } 44 | } 45 | ?> -------------------------------------------------------------------------------- /handler/server/reboot.php: -------------------------------------------------------------------------------- 1 | assign('error', I18N::get('You have no permissions for this action!')); 18 | return; 19 | } 20 | 21 | if(defined('DEMO') && DEMO) { 22 | $this->assign('error', I18N::get('DEMO-VERSION: This action can\'t be used!')); 23 | return; 24 | } 25 | 26 | $this->assign('success', I18N::get('The System will be rebooted now!')); 27 | $template->getCore()->setSettings('REBOOT', date('Y-m-d H:i:s', time())); 28 | $template->getCore()->getHooks()->runAction('SERVER_REBOOT', time()); 29 | break; 30 | } 31 | } 32 | 33 | $rebooting = $template->getCore()->getSettings('REBOOT', null); 34 | 35 | if(!empty($rebooting)) { 36 | $this->assign('success', I18N::get('The System will be rebooted now!').sprintf('

%s

', $rebooting)); 37 | } 38 | ?> -------------------------------------------------------------------------------- /classes/Hardware/NetworkFamily.enum.php: -------------------------------------------------------------------------------- 1 | value){ 20 | return $status; 21 | } 22 | } 23 | 24 | throw new \ValueError("$name is not a valid backing value for enum " . self::class); 25 | } 26 | 27 | public static function for(NetworkFamily $name) : string { 28 | foreach(self::cases() as $status) { 29 | if($status->name === $name->name) { 30 | return $status->name; 31 | } 32 | } 33 | 34 | throw new \ValueError("$name is not a valid backing value for enum " . self::class); 35 | } 36 | 37 | public static function tryFromName(string $name) : NetworkFamily | null { 38 | try { 39 | return self::fromName($name); 40 | } catch (\ValueError $error) { 41 | return null; 42 | } 43 | } 44 | } 45 | ?> 46 | -------------------------------------------------------------------------------- /default/js/codemirror/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | CodeMirror 6 7 | 9 | 10 | 11 |
12 | 13 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /classes/Installer/Installer.class.php: -------------------------------------------------------------------------------- 1 | getRepositorys($url); 16 | } 17 | 18 | public static function getRepository(int $id) { 19 | return Repository::getInstance()->getRepository($id); 20 | } 21 | 22 | public static function createRepository(string $url) : void { 23 | Repository::getInstance()->createRepository($url); 24 | } 25 | 26 | public static function updateRepository(string $id, string $timestamp) : void { 27 | Repository::getInstance()->updateRepository($id, $timestamp); 28 | } 29 | 30 | public static function deleteRepository(int $id) : void { 31 | Repository::getInstance()->deleteRepository($id); 32 | } 33 | 34 | public static function getFile($repository, $file) : string | int | null { 35 | return Repository::getInstance()->getFile($repository, $file); 36 | } 37 | 38 | public static function getRepositorysByID(array $ids) : array { 39 | return Repository::getInstance()->getRepositorysByID($ids); 40 | } 41 | } 42 | ?> -------------------------------------------------------------------------------- /libraries/PHPMailer/Exception.php: -------------------------------------------------------------------------------- 1 | 9 | * @author Jim Jagielski (jimjag) 10 | * @author Andy Prevost (codeworxtech) 11 | * @author Brent R. Matzelle (original founder) 12 | * @copyright 2012 - 2017 Marcus Bointon 13 | * @copyright 2010 - 2012 Jim Jagielski 14 | * @copyright 2004 - 2009 Andy Prevost 15 | * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License 16 | * @note This program is distributed in the hope that it will be useful - WITHOUT 17 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 18 | * FITNESS FOR A PARTICULAR PURPOSE. 19 | */ 20 | 21 | namespace PHPMailer; 22 | 23 | /** 24 | * PHPMailer exception handler. 25 | * 26 | * @author Marcus Bointon 27 | */ 28 | class Exception extends \Exception 29 | { 30 | /** 31 | * Prettify error message output. 32 | * 33 | * @return string 34 | */ 35 | public function errorMessage() 36 | { 37 | return '' . htmlspecialchars($this->getMessage()) . "
\n"; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/BUG.yml: -------------------------------------------------------------------------------- 1 | name: 🐞 Bug Report 2 | description: Make a bug report and tell us about an error/bug if you found one. 3 | title: "[Bug] " 4 | labels: ["bug", "triage"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for taking the time to fill out this bug report! 10 | Please **dont share** personal contact data (for sample `E-Mail` or `Discord`-Name), we never contact you here because we dont offer premium-support. 11 | - type: textarea 12 | id: what-happened 13 | attributes: 14 | label: What happened? 15 | description: Also tell us, what did you expect to happen? 16 | placeholder: Tell us what you see! 17 | value: "A bug happened!" 18 | validations: 19 | required: true 20 | - type: textarea 21 | id: logs 22 | attributes: 23 | label: Relevant log output 24 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 25 | render: shell 26 | - type: checkboxes 27 | id: terms 28 | attributes: 29 | label: Code of Conduct 30 | description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/fruithost/Panel/blob/master/.github/CODE_OF_CONDUCT.md) 31 | options: 32 | - label: I agree to follow this project's Code of Conduct 33 | required: true 34 | -------------------------------------------------------------------------------- /classes/Modules/ModuleError.class.php: -------------------------------------------------------------------------------- 1 | module = $module; 22 | $this->message = $exception->getMessage(); 23 | $this->code = $exception->getCode(); 24 | $this->file = $exception->getFile(); 25 | $this->line = $exception->getLine(); 26 | $this->trace = $exception->getTrace(); 27 | } 28 | 29 | public function getModule() : ?string { 30 | return $this->module; 31 | } 32 | 33 | public function getMessage() : ?string { 34 | return $this->message; 35 | } 36 | 37 | public function getCode() : ?string { 38 | return $this->code; 39 | } 40 | 41 | public function getFile() : ?string { 42 | return $this->file; 43 | } 44 | 45 | public function getLine() : ?string { 46 | return $this->line; 47 | } 48 | 49 | public function getTrace() : array { 50 | return $this->trace; 51 | } 52 | } 53 | ?> -------------------------------------------------------------------------------- /default/css/global.css: -------------------------------------------------------------------------------- 1 | /* 2 | * fruithost | OpenSource Hosting 3 | * 4 | * @author Adrian Preuß 5 | * @version 1.0.0 6 | * @license MIT 7 | */ 8 | 9 | html, body { 10 | height: 100%; 11 | } 12 | 13 | textarea:focus, 14 | textarea:focus, 15 | input:focus { 16 | outline: none; 17 | box-shadow: none !important; 18 | } 19 | 20 | /* Dropdown */ 21 | .dropdown-scroll { 22 | max-height: 145px; 23 | overflow-y: scroll; 24 | } 25 | 26 | .btn-text:hover, 27 | .btn-text:active, 28 | .btn-text:focus { 29 | color: var(--bs-body-color) !important; 30 | background-color: transparent !important; 31 | border-color: transparent !important; 32 | cursor: normal !important; 33 | } 34 | 35 | .text-justify { 36 | text-align: justify; 37 | text-justify: inter-word; 38 | } 39 | 40 | @keyframes dots { 41 | 0% { 42 | content: ""; 43 | } 44 | 45 | 20% { 46 | content: "."; 47 | } 48 | 49 | 40% { 50 | content: ".."; 51 | } 52 | 53 | 80% { 54 | content: "..."; 55 | } 56 | 57 | 100% { 58 | content: ""; 59 | } 60 | } 61 | 62 | span[data-dots="true"] { 63 | display: inline-block; 64 | text-align: left; 65 | min-width: 10px; 66 | } 67 | 68 | span[data-dots="true"]::after { 69 | content: ""; 70 | width: 5px; 71 | animation: dots 3s linear infinite; 72 | } -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | 2 | Header append Vary Accept-Encoding 3 | Header append Vary User-Agent 4 | Header set Connection keep-alive 5 | Header unset ETag 6 | FileETag None 7 | 8 | 9 | Header set Cache-Control "max-age=31536000" 10 | 11 | 12 | Header set Access-Control-Allow-Origin * 13 | 14 | 15 | 16 | 17 | AddOutputFilterByType DEFLATE text/plain 18 | AddOutputFilterByType DEFLATE text/html 19 | AddOutputFilterByType DEFLATE text/xml 20 | AddOutputFilterByType DEFLATE text/css 21 | AddOutputFilterByType DEFLATE application/xml 22 | AddOutputFilterByType DEFLATE application/xhtml+xml 23 | AddOutputFilterByType DEFLATE application/rss+xml 24 | AddOutputFilterByType DEFLATE application/javascript 25 | AddOutputFilterByType DEFLATE application/x-javascript 26 | 27 | 28 | 29 | 30 | 31 | Options -MultiViews 32 | 33 | 34 | RewriteEngine On 35 | 36 | # Redirect Trailing Slashes 37 | RewriteRule ^(.*)/$ /$1 [L,R=301] 38 | 39 | # Handle Template Files 40 | RewriteRule ^theme/([^/]+)/(.*)$ index.php?theme=$1&file=$2 [QSA,L] 41 | RewriteRule ^(css|js|fonts|images|assets)/(.*)$ default/$1/$2 [QSA,L] 42 | 43 | # Handle Front Controller 44 | RewriteCond %{REQUEST_FILENAME} !-d 45 | RewriteCond %{REQUEST_FILENAME} !-f 46 | RewriteRule ^ index.php [QSA,L] 47 | -------------------------------------------------------------------------------- /handler/settings/security.php: -------------------------------------------------------------------------------- 1 | getCore()->getHooks()->applyFilter('2FA_METHODS', [ 16 | (object) [ 17 | 'id' => 'mail', 18 | 'name' => I18N::get('E-Mail'), 19 | 'enabled' => true 20 | ] 21 | ]) AS $index => $method) { 22 | if($method->id == $_POST['2fa_type']) { 23 | $selected = $method; 24 | break; 25 | } 26 | } 27 | 28 | if($selected == null) { 29 | $template->assign('error', I18N::get('The selected 2FA method is not known.')); 30 | return; 31 | } 32 | 33 | if(!$selected->enabled) { 34 | $template->assign('error', sprintf(I18N::get('The 2FA method "%s" is currently deactivated and can therefore not be used.'), $selected->name)); 35 | return; 36 | } 37 | 38 | if(isset($_POST['2fa_enabled'])) { 39 | Auth::setSettings('2FA_ENABLED', NULL, 'true'); 40 | } else { 41 | Auth::setSettings('2FA_ENABLED', NULL, 'false'); 42 | } 43 | 44 | Auth::setSettings('2FA_TYPE', NULL, $_POST['2fa_type']); 45 | 46 | $template->getCore()->getHooks()->runAction('SAVE_ACCOUNT_SETTINGS_SECURITY', $_POST); 47 | $template->assign('success', I18N::get('Your security settings has been updated.')); 48 | ?> -------------------------------------------------------------------------------- /classes/Network/ResponseFactory.class.php: -------------------------------------------------------------------------------- 1 | 'text/html; charset=UTF-8' 16 | ]; 17 | 18 | public static function getInstance() : ResponseFactory { 19 | if(self::$instance === null) { 20 | self::$instance = new self(); 21 | } 22 | 23 | return self::$instance; 24 | } 25 | 26 | private function __construct() { 27 | if(defined('DEBUG') && DEBUG) { 28 | $this->addHeader('DEBUG', DEBUG); 29 | } 30 | 31 | if(isset($_SERVER['HTTP_ACCEPT_ENCODING']) && substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')) { 32 | //$this->addHeader('Content-Encoding', 'gzip'); 33 | // @ToDo Debug output encoding 34 | } 35 | } 36 | 37 | public function setContentType(string $type) : void { 38 | $this->headers['Content-Type'] = $type; 39 | } 40 | 41 | public function addHeader(string $name, string $value) : void { 42 | $this->headers[$name] = $value; 43 | } 44 | 45 | public function header() : void { 46 | if(count($this->headers) > 0) { 47 | foreach($this->headers AS $name => $value) { 48 | header(sprintf('%s: %s', $name, $value)); 49 | } 50 | } 51 | } 52 | 53 | public function redirect(string $url) : void { 54 | $this->addHeader('Location', $url); 55 | $this->header(); 56 | exit(); 57 | } 58 | } 59 | 60 | ?> -------------------------------------------------------------------------------- /classes/Accounting/Session.class.php: -------------------------------------------------------------------------------- 1 | 3600, 19 | //'read_and_close' => true 20 | ]); 21 | } 22 | } 23 | 24 | public static function debug() : void { 25 | var_dump([ 26 | 'HTTP_COOKIE' => $_SERVER['HTTP_COOKIE'], 27 | 'COOKIE' => $_COOKIE, 28 | 'session_id' => session_id(), 29 | 'data' => $_SESSION 30 | ]); 31 | } 32 | 33 | public static function getID() : string | false { 34 | return session_id(); 35 | } 36 | 37 | public static function has(string $name) : bool { 38 | self::init(); 39 | 40 | if(isset($_SESSION[$name])) { 41 | return true; 42 | } 43 | 44 | return false; 45 | } 46 | 47 | public static function get(string $name) : mixed { 48 | self::init(); 49 | 50 | if(isset($_SESSION[$name])) { 51 | return $_SESSION[$name]; 52 | } 53 | 54 | return null; 55 | } 56 | 57 | public static function set(string $name, mixed $value) : void { 58 | self::init(); 59 | 60 | $_SESSION[$name] = $value; 61 | } 62 | 63 | public static function remove(string $name) : void { 64 | self::init(); 65 | 66 | $_SESSION[$name] = null; 67 | unset($_SESSION[$name]); 68 | } 69 | } 70 | ?> -------------------------------------------------------------------------------- /classes/UI/Button.class.php: -------------------------------------------------------------------------------- 1 | name; 21 | } 22 | 23 | public function setName(string $name) : Button { 24 | $this->name = $name; 25 | return $this; 26 | } 27 | 28 | public function isDismissable() : bool { 29 | return $this->dismissable; 30 | } 31 | 32 | public function setDismissable() : Button { 33 | $this->dismissable = true; 34 | return $this; 35 | } 36 | 37 | public function hasModal() : bool { 38 | return !empty($this->modal); 39 | } 40 | 41 | public function getModal() : ?string { 42 | return $this->modal; 43 | } 44 | 45 | public function setModal(string $modal) : Button { 46 | $this->modal = $modal; 47 | return $this; 48 | } 49 | 50 | public function getLabel() : ?string { 51 | return $this->label; 52 | } 53 | 54 | public function setLabel(string $label) : Button { 55 | $this->label = $label; 56 | return $this; 57 | } 58 | 59 | public function getClasses(bool $raw = true) : string | array { 60 | if($raw) { 61 | return $this->classes; 62 | } 63 | 64 | return implode(' ', $this->classes); 65 | } 66 | 67 | public function addClass(string $class) : Button { 68 | $this->classes[] = $class; 69 | return $this; 70 | } 71 | } 72 | ?> -------------------------------------------------------------------------------- /libraries/skoerfgen/src/ACME_Exception.php: -------------------------------------------------------------------------------- 1 | type=$type; 37 | $this->subproblems=$subproblems; 38 | parent::__construct($detail.' ('.$type.')'); 39 | } 40 | function getType(){ 41 | return $this->type; 42 | } 43 | function getSubproblems(){ 44 | return $this->subproblems; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /libraries/Sepia/PoParser/Catalog/Header.php: -------------------------------------------------------------------------------- 1 | setHeaders($headers); 16 | } 17 | 18 | public function getPluralFormsCount() 19 | { 20 | if ($this->nPlurals !== null) { 21 | return $this->nPlurals; 22 | } 23 | 24 | $header = $this->getHeaderValue('Plural-Forms'); 25 | if ($header === null) { 26 | $this->nPlurals = 0; 27 | return $this->nPlurals; 28 | } 29 | 30 | $matches = array(); 31 | if (preg_match('/nplurals=([0-9]+)/', $header, $matches) !== 1) { 32 | $this->nPlurals = 0; 33 | return $this->nPlurals; 34 | } 35 | 36 | $this->nPlurals = isset($matches[1]) ? (int)$matches[1] : 0; 37 | 38 | return $this->nPlurals; 39 | } 40 | 41 | public function setHeaders(array $headers) 42 | { 43 | $this->headers = $headers; 44 | } 45 | 46 | /** 47 | * @return array 48 | */ 49 | public function asArray() 50 | { 51 | return $this->headers; 52 | } 53 | 54 | /** 55 | * @param string $headerName 56 | * 57 | * @return string|null 58 | */ 59 | protected function getHeaderValue($headerName) 60 | { 61 | $header = array_values(array_filter( 62 | $this->headers, 63 | function ($string) use ($headerName) { 64 | return preg_match('/'.$headerName.':(.*)/i', $string) == 1; 65 | } 66 | )); 67 | 68 | return count($header) ? $header[0] : null; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /default/server/reboot.php: -------------------------------------------------------------------------------- 1 | header(); 15 | if(!Auth::hasPermission('SERVER::REBOOT')) { 16 | ?> 17 | 21 | footer(); 23 | exit(); 24 | } 25 | ?> 26 |
27 | [ 'text-warning' ] 30 | ]); 31 | ?> 32 |

33 | 36 |

37 | 40 |

41 | 44 |

45 |
46 | 48 |
49 | 52 |
53 | footer(); 55 | ?> -------------------------------------------------------------------------------- /default/error/module_empty.php: -------------------------------------------------------------------------------- 1 | header(); 14 | ?> 15 | 36 |
37 | 38 |

39 |

40 |
41 | footer(); 43 | ?> -------------------------------------------------------------------------------- /default/error/module.php: -------------------------------------------------------------------------------- 1 | header(); 14 | ?> 15 | 36 |
37 | 38 |

39 |

40 |
41 | footer(); 43 | ?> -------------------------------------------------------------------------------- /default/error/permissions.php: -------------------------------------------------------------------------------- 1 | header(); 14 | ?> 15 | 36 |
37 | 38 |

39 |

40 |
41 | footer(); 43 | ?> -------------------------------------------------------------------------------- /classes/Network/RequestFactory.class.php: -------------------------------------------------------------------------------- 1 | _get)) { 18 | $this->init(); 19 | } 20 | } 21 | 22 | public static function getInstance() : RequestFactory { 23 | if(self::$instance === null) { 24 | self::$instance = new self(); 25 | } 26 | 27 | return self::$instance; 28 | } 29 | 30 | public function init() : void { 31 | if(!empty($this->_get)) { 32 | return; 33 | } 34 | 35 | // Fix bad behavior of Apache's Alias configguration 36 | if(empty($_SERVER['QUERY_STRING']) && isset($_SERVER['REQUEST_URI']) && strtok($_SERVER['REQUEST_URI'], '?') !== false) { 37 | $split = explode('?', $_SERVER['REQUEST_URI']); 38 | 39 | if(count($split) > 1) { 40 | $_SERVER['QUERY_STRING'] = $split[1]; 41 | parse_str($_SERVER['QUERY_STRING'], $_GET); 42 | } 43 | } 44 | 45 | $this->_get = $_GET; 46 | } 47 | 48 | public function has(string $name) : bool { 49 | return array_key_exists($name, $this->_get); 50 | } 51 | 52 | public function get(string $name) : mixed { 53 | if(!$this->has($name)) { 54 | return null; 55 | } 56 | 57 | return $this->_get[$name]; 58 | } 59 | 60 | public function url() : string { 61 | return $_SERVER['REQUEST_URI']; 62 | } 63 | } 64 | ?> -------------------------------------------------------------------------------- /default/js/jquery-ui/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright jQuery Foundation and other contributors, https://jquery.org/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery-ui 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | Copyright and related rights for sample code are waived via CC0. Sample 34 | code is defined as all source code contained within the demos directory. 35 | 36 | CC0: http://creativecommons.org/publicdomain/zero/1.0/ 37 | 38 | ==== 39 | 40 | All files located in the node_modules and external directories are 41 | externally maintained libraries used by this software which have their 42 | own licenses; we recommend you read them, as their terms may differ from 43 | the terms above. 44 | -------------------------------------------------------------------------------- /libraries/Sepia/PoParser/Catalog/EntryFactory.php: -------------------------------------------------------------------------------- 1 | $value) { 20 | switch (true) { 21 | case $key === 'msgctxt': 22 | $entry->setMsgCtxt($entryArray['msgctxt']); 23 | break; 24 | 25 | case $key === 'flags': 26 | $entry->setFlags($entryArray['flags']); 27 | break; 28 | 29 | case $key === 'reference': 30 | $entry->setReference($entryArray['reference']); 31 | break; 32 | 33 | case $key === 'previous': 34 | $entry->setPreviousEntry(self::createFromArray($entryArray['previous'])); 35 | break; 36 | 37 | case $key === 'tcomment': 38 | $entry->setTranslatorComments($value); 39 | break; 40 | 41 | case $key === 'ccomment': 42 | $entry->setDeveloperComments($value); 43 | break; 44 | 45 | case $key === 'obsolete': 46 | $entry->setObsolete(true); 47 | break; 48 | 49 | case 0 === strpos($key, 'msgstr['): 50 | $plurals[] = $value; 51 | break; 52 | } 53 | } 54 | 55 | if (count($plurals) > 0) { 56 | $entry->setMsgStrPlurals($plurals); 57 | if(!empty($entryArray['msgid_plural'])){ 58 | $entry->setMsgIdPlural($entryArray['msgid_plural']); 59 | } 60 | } 61 | 62 | return $entry; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /libraries/Sepia/PoParser/SourceHandler/SourceHandler.php: -------------------------------------------------------------------------------- 1 | name = $name; 25 | $this->title = $title; 26 | $this->variables = $variables; 27 | 28 | if(is_string($content)) { 29 | $this->content = $content; 30 | } 31 | } 32 | 33 | public function getCallback(string $name) : mixed { 34 | if(!isset($this->callbacks[$name])) { 35 | return null; 36 | } 37 | 38 | return $this->callbacks[$name]; 39 | } 40 | 41 | public function onSave(mixed $method) : Modal { 42 | $this->callbacks['save'] = $method; 43 | 44 | return $this; 45 | } 46 | 47 | public function getName() : ?string { 48 | return $this->name; 49 | } 50 | 51 | public function getTitle() : ?string { 52 | return $this->title; 53 | } 54 | 55 | public function isShowing() : bool { 56 | return $this->show; 57 | } 58 | 59 | public function addButton(array $button) : Modal { 60 | $this->buttons[] = $button; 61 | return $this; 62 | } 63 | 64 | public function getButtons() : array { 65 | return $this->buttons; 66 | } 67 | 68 | public function getContent(Template $template) : mixed { 69 | foreach($this->variables AS $name => $value) { 70 | ${$name} = $value; 71 | } 72 | 73 | // @ToDo check if is template file 74 | if(!empty($this->content)) { 75 | $template->display($this->content, $this->variables, true, false); 76 | return null; 77 | } 78 | 79 | return $this->content; 80 | } 81 | 82 | public function show($variables = []) { 83 | $this->variables = array_merge($this->variables, $variables); 84 | $this->show = true; 85 | } 86 | } 87 | ?> -------------------------------------------------------------------------------- /classes/Hardware/NetworkAddress.class.php: -------------------------------------------------------------------------------- 1 | family)) { 24 | $this->type = NetworkFamily::tryFromName($data->family); 25 | } 26 | 27 | if(isset($data->label)) { 28 | $this->name = $data->label; 29 | } 30 | 31 | // IP-Address 32 | if(isset($data->local)) { 33 | $this->address = $data->local; 34 | } 35 | 36 | // Integer - Subnetting 37 | if(isset($data->prefixlen)) { 38 | $this->prefix = $data->prefixlen; 39 | } 40 | 41 | // IP-Address 42 | if(isset($data->broadcast)) { 43 | $this->broadcast = $data->broadcast; 44 | } 45 | 46 | // @see https://github.com/iproute2/iproute2/blob/main/ip/ip_common.h#L219 47 | // INFINITY_LIFE_TIME = 0xFFFFFFFFU, unsigned 48 | 49 | // Int - Timestamp 50 | if(isset($data->valid_life_time)) { 51 | $this->time_ttl = $data->valid_life_time; 52 | } 53 | 54 | // Int - Timestamp 55 | if(isset($data->valid_life_time)) { 56 | $this->time_valid = $data->valid_life_time; 57 | } 58 | } 59 | 60 | public function getName() : string { 61 | return $this->name; 62 | } 63 | 64 | public function getFamily() : NetworkFamily { 65 | return $this->type; 66 | } 67 | 68 | public function getAddress() : string { 69 | return $this->address; 70 | } 71 | 72 | public function getPrefix() : int { 73 | return $this->prefix; 74 | } 75 | 76 | public function getBroadcast() : string { 77 | return $this->broadcast; 78 | } 79 | 80 | public function getTTL() : int { 81 | return $this->time_ttl; 82 | } 83 | 84 | public function getValidTTL() : int { 85 | return $this->time_valid; 86 | } 87 | } 88 | ?> -------------------------------------------------------------------------------- /default/icons.json: -------------------------------------------------------------------------------- 1 | { 2 | "template": { 3 | "element": "i", 4 | "classes": [ 5 | "bi" 6 | ], 7 | "content": { 8 | "type": "class", 9 | "html": "bi-%s" 10 | } 11 | }, 12 | "definitions": { 13 | "language": "translate", 14 | "smiley-bad": "emoji-frown", 15 | "smiley-smile": "emoji-smile-upside-down", 16 | "accessibility": "universal-access-circle", 17 | "list": "list", 18 | "theme-light": "sun-fill", 19 | "theme-dark": "moon-stars-fill", 20 | "theme-auto": "circle-half", 21 | "check": "check2", 22 | "okay": "check-circle", 23 | "error": "x-circle-fill", 24 | "github": "github", 25 | "dot": "dot", 26 | "grid": "grid", 27 | "clock": "clock", 28 | "bookmark-star": "bookmark-star", 29 | "ftp": "folder", 30 | "database": "database", 31 | "mailbox": "envelope", 32 | "settings": "gear", 33 | "file": "card-heading", 34 | "ip": "router", 35 | "uptime": "clock", 36 | "unknown": "question", 37 | "memory": "memory", 38 | "storage": "hdd", 39 | "network": "pci-card-network", 40 | "networking": "diagram-2", 41 | "account": "person-circle", 42 | "logout": "power", 43 | "overview": "speedometer", 44 | "users": "people-fill", 45 | "themes": "palette", 46 | "modules": "boxes", 47 | "server": "device-hdd", 48 | "logfiles": "file-earmark-text", 49 | "options": "sliders", 50 | "console": "terminal-fill", 51 | "packages": "archive-fill", 52 | "services": "activity", 53 | "certificate": "file-medical", 54 | "self-signed": "highlighter", 55 | "domains": "globe-europe-africa", 56 | "ssl-valid": "shield-fill-check", 57 | "ssl-invalid": "shield-fill-x", 58 | "arrow-down": "caret-down-square-fill", 59 | "arrow-right": "chevron-compact-right", 60 | "secure": "file-lock2", 61 | "insecure": "shield-lock", 62 | "show": "eye", 63 | "hide": "eye-slash-fill", 64 | "start": "play-fill", 65 | "stop": "stop-fill", 66 | "restart": "arrow-clockwise", 67 | "warning": "exclamation-triangle-fill", 68 | "update": "arrow-repeat", 69 | "reboot": "arrow-repeat", 70 | "delete": "trash-fill", 71 | "info": "info-circle-fill" 72 | } 73 | } -------------------------------------------------------------------------------- /classes/Accounting/Auth.class.php: -------------------------------------------------------------------------------- 1 | isLoggedIn(); 15 | } 16 | 17 | public static function TwoFactorLogin(string $username, #[\SensitiveParameter] string $password) : bool { 18 | return AuthFactory::getInstance()->TwoFactorLogin($username, $password); 19 | } 20 | 21 | public static function login(string $username, #[\SensitiveParameter] string $password) : bool { 22 | return AuthFactory::getInstance()->login($username, $password); 23 | } 24 | 25 | public static function logout() : bool { 26 | return AuthFactory::getInstance()->logout(); 27 | } 28 | 29 | public static function getID() : ?int { 30 | return AuthFactory::getInstance()->getID(); 31 | } 32 | 33 | public static function getUsername() : ?string { 34 | return AuthFactory::getInstance()->getUsername(); 35 | } 36 | 37 | public static function getMail() : ?string { 38 | return AuthFactory::getInstance()->getMail(); 39 | } 40 | 41 | public static function getGravatar() : string { 42 | return AuthFactory::getInstance()->getGravatar(); 43 | } 44 | 45 | public static function setSettings(string $name, int | string | null $user_id = null, mixed $value = null) : void { 46 | AuthFactory::getInstance()->setSettings($name, $user_id, $value); 47 | } 48 | 49 | public static function getSettings(string $name, int | string | null $user_id = null, mixed $default = null) : mixed { 50 | return AuthFactory::getInstance()->getSettings($name, $user_id, $default); 51 | } 52 | 53 | public static function removeSettings(string $name, int | string | null $user_id = null) : void { 54 | AuthFactory::getInstance()->removeSettings($name, $user_id); 55 | } 56 | 57 | public static function hasPermission(string $name) : bool { 58 | return AuthFactory::getInstance()->hasPermission($name); 59 | } 60 | 61 | public static function getPermissions() : array { 62 | return AuthFactory::getInstance()->getPermissions(); 63 | } 64 | } 65 | ?> -------------------------------------------------------------------------------- /default/admin/users/create.php: -------------------------------------------------------------------------------- 1 | header(); 13 | 14 | if(isset($error)) { 15 | ?> 16 | 17 | 20 | 21 | 24 |
25 |
26 | 27 |
28 | 29 |
30 |
31 |
32 | 33 |
34 | 35 |
36 |
37 | 38 |
39 | 40 |
41 | 42 |
43 | 44 |
45 |
46 |
47 | 48 |
49 | 50 |
51 |
52 | 53 |
54 | 55 |
56 |
57 | footer(); 59 | ?> -------------------------------------------------------------------------------- /handler/server/network.php: -------------------------------------------------------------------------------- 1 | assign('network', NetworkInterfaces::get()); 15 | 16 | switch($action) { 17 | case 'start': 18 | if(!Auth::hasPermission('SERVER::NETWORK')) { 19 | $this->assign('error', I18N::get('You have no permissions for this action!')); 20 | return; 21 | } 22 | 23 | if(defined('DEMO') && DEMO) { 24 | $this->assign('error', I18N::get('DEMO-VERSION: This action can\'t be used!')); 25 | return; 26 | } 27 | 28 | $device = NetworkInterfaces::getDevice($tab); 29 | 30 | if(empty($device)) { 31 | $this->assign('error', sprintf(I18N::get('Unknown Network-Interface: %s'), $tab)); 32 | return; 33 | } 34 | 35 | $this->assign('success', sprintf(I18N::get('The Network-Interface "%s" will be started!'), $tab)); 36 | $device->enable(); 37 | break; 38 | case 'stop': 39 | if(!Auth::hasPermission('SERVER::NETWORK')) { 40 | $this->assign('error', I18N::get('You have no permissions for this action!')); 41 | return; 42 | } 43 | 44 | if(defined('DEMO') && DEMO) { 45 | $this->assign('error', I18N::get('DEMO-VERSION: This action can\'t be used!')); 46 | return; 47 | } 48 | 49 | $device = NetworkInterfaces::getDevice($tab); 50 | 51 | if(empty($device)) { 52 | $this->assign('error', sprintf(I18N::get('Unknown Network-Interface: %s'), $tab)); 53 | return; 54 | } 55 | 56 | $this->assign('success', sprintf(I18N::get('The Network-Interface "%s" will be stopped!'), $tab)); 57 | $device->disable(); 58 | break; 59 | case 'info': 60 | if(!Auth::hasPermission('SERVER::NETWORK')) { 61 | $this->assign('error', I18N::get('You have no permissions for this action!')); 62 | return; 63 | } 64 | 65 | $device = NetworkInterfaces::getDevice($tab); 66 | 67 | if(empty($device)) { 68 | $this->assign('error', sprintf(I18N::get('Unknown Network-Interface: %s'), $tab)); 69 | return; 70 | } 71 | 72 | $this->assign('device', $device); 73 | break; 74 | } 75 | ?> -------------------------------------------------------------------------------- /default/admin/users/edit/settings.php: -------------------------------------------------------------------------------- 1 | 12 |

13 |
14 |
15 | 16 |
17 | 24 |
25 |
26 |
27 |
28 | 29 |
30 | 31 |
32 |
33 |
34 | 35 |
36 | 49 |
50 |
51 | getCore()->getHooks()->runAction('ACCOUNT_SETTINGS_GLOBAL', [ 53 | 'user' => $user 54 | ]); 55 | ?> 56 |
57 | 58 |
59 |
-------------------------------------------------------------------------------- /classes/Storage/Database.class.php: -------------------------------------------------------------------------------- 1 | file($file, $callback); 15 | } 16 | 17 | public static function getError(?object $object = null): array { 18 | return DatabaseFactory::getInstance()->getError($object); 19 | } 20 | 21 | public static function query(string $query, array $parameters = []) : \PDOStatement | false { 22 | return DatabaseFactory::getInstance()->query($query, null, $parameters); 23 | } 24 | 25 | public static function single(string $query, array $parameters = []) : mixed { 26 | return DatabaseFactory::getInstance()->single($query, $parameters); 27 | } 28 | 29 | public static function count(string $query, array $parameters = []) : int { 30 | return DatabaseFactory::getInstance()->count($query, $parameters); 31 | } 32 | 33 | public static function exists(string $query, array $parameters = []) : bool { 34 | return DatabaseFactory::getInstance()->count($query, $parameters) > 0; 35 | } 36 | 37 | public static function fetch(string $query, array $parameters = []) : array | false { 38 | return DatabaseFactory::getInstance()->fetch($query, $parameters); 39 | } 40 | 41 | public static function update(string $table, string | array $where, array $parameters = []) : array | false { 42 | return DatabaseFactory::getInstance()->update($table, $where, $parameters); 43 | } 44 | 45 | public static function insert(string $table, array $parameters = []) : int { 46 | return DatabaseFactory::getInstance()->insert($table, $parameters); 47 | } 48 | 49 | public static function delete(string $table, array $parameters = []) : \PDOStatement | false { 50 | return DatabaseFactory::getInstance()->delete($table, $parameters); 51 | } 52 | 53 | public static function tableExists(string $table) : bool { 54 | return DatabaseFactory::getInstance()->tableExists($table); 55 | } 56 | 57 | public static function deleteWhereNot(string $table, array $delete_not = [], array $parameters = []) : \PDOStatement | false { 58 | return DatabaseFactory::getInstance()->deleteWhereNot($table, $delete_not, $parameters); 59 | } 60 | } 61 | ?> 62 | -------------------------------------------------------------------------------- /classes/System/CoreAdmin.class.php: -------------------------------------------------------------------------------- 1 | getCore()->getHooks()->runAction('core_admin_pre_init'); 22 | 23 | $this->addModal((new Modal('confirmation', I18N::get('Confirmation'), null))->addButton([ 24 | (new Button())->setName('cancel')->setLabel(I18N::get('No'))->addClass('btn-outline-danger')->setDismissable(), 25 | (new Button())->setName('create')->setLabel(I18N::get('Yes'))->addClass('btn-outline-success') 26 | ])->onSave(function(array $data = []) : ?string { 27 | // @ToDo Check permissions? 28 | return 'CONFIRMED'; 29 | })); 30 | 31 | $this->getRouter()->addRoute('/admin', function() { 32 | if(!Auth::isLoggedIn()) { 33 | Response::redirect('/'); 34 | } 35 | 36 | $this->getTemplate()->display('overview', [ 37 | 'admin' => true 38 | ]); 39 | }); 40 | 41 | $this->getRouter()->addRoute('^/admin(?:(?:/([a-zA-Z0-9\-_]+)?)(?:/([a-zA-Z0-9\-_]+)(?:/([a-zA-Z0-9\-_]+))?)?)?$', function(?string $destination = null, ?string $tab = null, ?string $action = null) { 42 | if(!Auth::isLoggedIn()) { 43 | Response::redirect('/'); 44 | } 45 | 46 | if($this->getTemplate()->display((!empty($destination) ? sprintf('/admin/%s', $destination) : 'overview'), [ 47 | 'tab' => $tab, 48 | 'action' => $action, 49 | 'admin' => true 50 | ]) === false) { 51 | $this->getTemplate()->display('error/404'); 52 | } 53 | }); 54 | 55 | $this->getRouter()->addRoute('^/server(?:/([a-zA-Z0-9\-_]+))(?:/([a-zA-Z0-9\-_]+))?(?:/([a-zA-Z0-9\-_]+))?$', function(?string $destination = null, ?string $tab = null, ?string $action = null) { 56 | if(!Auth::isLoggedIn()) { 57 | Response::redirect('/'); 58 | } 59 | 60 | if($this->getTemplate()->display('server' . (!empty($destination) ? sprintf('/%s', $destination) : ''), [ 61 | 'tab' => $tab, 62 | 'action' => $action 63 | ]) === false) { 64 | $this->getTemplate()->display('error/404'); 65 | } 66 | }); 67 | } 68 | } 69 | ?> -------------------------------------------------------------------------------- /default/js/components/confirmation.js: -------------------------------------------------------------------------------- 1 | /** 2 | * fruithost | OpenSource Hosting 3 | * 4 | * @author Adrian Preuß 5 | * @version 1.0.0 6 | * @license MIT 7 | */ 8 | 9 | (() => { 10 | window.Confirm = function Confirm(element) { 11 | element.addEventListener('mousedown', function (event) { 12 | let target = event.target; 13 | let el = document.querySelector('#confirmation'); 14 | const popup = new bootstrap.Modal(el); 15 | let prevent= true; 16 | let header = popup._element.querySelector('.modal-header'); 17 | let a = document.createElement('p'); 18 | 19 | a.classList.add('m-0'); 20 | a.classList.add('text-center'); 21 | a.classList.add('p-3'); 22 | a.innerHTML = target.dataset.confirm; 23 | 24 | let b = document.createElement('p'); 25 | b.classList.add('modal-body'); 26 | b.classList.add('d-none'); 27 | b.innerHTML = target.dataset.confirm; 28 | 29 | [].map.call(header.parentNode.querySelectorAll('p'), function (e) { 30 | e.parentNode.removeChild(e); 31 | }); 32 | 33 | popup._element.querySelector('.modal-footer').classList.add('text-center'); 34 | header.parentNode.insertBefore(a, header.nextSibling); 35 | header.parentNode.insertBefore(b, header.nextSibling); 36 | 37 | popup.show(); 38 | 39 | let _watcher = setInterval(function () { 40 | var res = el.querySelector('.alert'); 41 | 42 | if(res == null || typeof (res) === 'undefined') { 43 | return; 44 | } 45 | 46 | var state = res.innerText; 47 | 48 | if(state === 'CONFIRMED') { 49 | clearInterval(_watcher); 50 | 51 | if(target.tagName === 'A') { 52 | window.location.href = target.href; 53 | } else { 54 | target.click(); 55 | } 56 | } 57 | 58 | }, 500); 59 | 60 | el.addEventListener('hide.bs.modal', function (event) { 61 | clearInterval(_watcher); 62 | }); 63 | 64 | if(prevent) { 65 | event.preventDefault(); 66 | return false; 67 | } 68 | }); 69 | }; 70 | 71 | [].map.call(document.querySelectorAll('[data-confirm]'), Confirm); 72 | })(); -------------------------------------------------------------------------------- /libraries/Sepia/PoParser/Catalog/CatalogArray.php: -------------------------------------------------------------------------------- 1 | entries = array(); 19 | $this->headers = new Header(); 20 | foreach ($entries as $entry) { 21 | $this->addEntry($entry); 22 | } 23 | } 24 | 25 | /** 26 | * {@inheritdoc} 27 | */ 28 | public function addEntry(Entry $entry) 29 | { 30 | $key = $this->getEntryHash( 31 | $entry->getMsgId(), 32 | $entry->getMsgCtxt() 33 | ); 34 | $this->entries[$key] = $entry; 35 | } 36 | 37 | /** 38 | * {@inheritdoc} 39 | */ 40 | public function addHeaders(Header $headers) 41 | { 42 | $this->headers = $headers; 43 | } 44 | 45 | /** 46 | * {@inheritdoc} 47 | */ 48 | public function removeEntry($msgid, $msgctxt = null) 49 | { 50 | $key = $this->getEntryHash($msgid, $msgctxt); 51 | if (isset($this->entries[$key])) { 52 | unset($this->entries[$key]); 53 | } 54 | } 55 | 56 | /** 57 | * {@inheritdoc} 58 | */ 59 | public function getHeaders() 60 | { 61 | return $this->headers->asArray(); 62 | } 63 | 64 | /** 65 | * {@inheritdoc} 66 | */ 67 | public function getHeader() 68 | { 69 | return $this->headers; 70 | } 71 | 72 | /** 73 | * {@inheritdoc} 74 | */ 75 | public function getEntries() 76 | { 77 | return $this->entries; 78 | } 79 | 80 | /** 81 | * {@inheritdoc} 82 | */ 83 | public function getEntry($msgId, $context = null) 84 | { 85 | $key = $this->getEntryHash($msgId, $context); 86 | if (!isset($this->entries[$key])) { 87 | return null; 88 | } 89 | 90 | return $this->entries[$key]; 91 | } 92 | 93 | /** 94 | * @param string $msgId 95 | * @param string|null $context 96 | * 97 | * @return string 98 | */ 99 | private function getEntryHash($msgId, $context = null) 100 | { 101 | return md5($msgId.$context); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /classes/Hardware/PhysicalDrives.class.php: -------------------------------------------------------------------------------- 1 | devices[] = [ 51 | 'name' => $matches[3], 52 | 'text' => $text, 53 | 'type' => $df[1], 54 | 'blocks' => $blocks, 55 | 'size' => (int) $df[2] * 1024, 56 | 'used' => (int) $df[3] * 1024, 57 | 'available' => (int) $df[4] * 1024, 58 | 'percent' => (int) $df[5], 59 | 'filesystem' => $df[6] 60 | ]; 61 | } 62 | } 63 | } 64 | 65 | public function getDevices() : array { 66 | return $this->devices; 67 | } 68 | } 69 | 70 | class PhysicalDrives { 71 | public static function get() : ?PhysicalDrivesFactory { 72 | return PhysicalDrivesFactory::getInstance(); 73 | } 74 | 75 | public static function getDevices() : array { 76 | return PhysicalDrivesFactory::getInstance()->getDevices(); 77 | } 78 | } 79 | ?> -------------------------------------------------------------------------------- /default/js/navigation.js: -------------------------------------------------------------------------------- 1 | /** 2 | * fruithost | OpenSource Hosting 3 | * 4 | * @author Adrian Preuß 5 | * @version 1.0.0 6 | * @license MIT 7 | */ 8 | 'use strict'; 9 | 10 | class Navigation { 11 | constructor() { 12 | let sidebar = document.querySelector('.sidebar'); 13 | 14 | [].map.call(document.querySelectorAll('.collapse'), (collapse) => { 15 | collapse.addEventListener('show.bs.collapse', this.show, false); 16 | collapse.addEventListener('shown.bs.collapse', this.show, false); 17 | collapse.addEventListener('hide.bs.collapse', this.hide, false); 18 | collapse.addEventListener('hidden.bs.collapse', this.hide, false); 19 | }); 20 | 21 | [].map.call(document.querySelectorAll('[data-bs-toggle="collapse"] a[href]'), (collapse) => { 22 | collapse.addEventListener('click', (event) => { 23 | window.location.href = event.target.href; 24 | }, false); 25 | }); 26 | 27 | window.addEventListener('resize', (event) => { 28 | if(window.getComputedStyle(sidebar, null).display !== 'block') { 29 | sidebar.classList.remove('show'); 30 | } 31 | }, true); 32 | 33 | document.querySelector('[data-bs-toggle="sidebar"]').addEventListener('click', (event) => { 34 | if(window.getComputedStyle(sidebar, null).display !== 'block') { 35 | sidebar.classList.add('show'); 36 | } else { 37 | sidebar.classList.remove('show'); 38 | } 39 | }); 40 | } 41 | 42 | show(event) { 43 | [].map.call(document.querySelectorAll('.bi[data-bs-toggle="collapse"][data-bs-target="#' + event.target.id + '"], [data-bs-toggle="collapse"][data-bs-target="#' + event.target.id + '"] .bi'), function (icon) { 44 | icon.classList.remove('bi-caret-up-square-fill'); 45 | icon.classList.remove('bi-caret-down-square-fill'); 46 | icon.classList.add('bi-caret-up-square-fill'); 47 | }); 48 | } 49 | 50 | hide(event) { 51 | [].map.call(document.querySelectorAll('.bi[data-bs-toggle="collapse"][data-bs-target="#' + event.target.id + '"], [data-bs-toggle="collapse"][data-bs-target="#' + event.target.id + '"] .bi'), function (icon) { 52 | icon.classList.remove('bi-caret-up-square-fill'); 53 | icon.classList.remove('bi-caret-down-square-fill'); 54 | icon.classList.add('bi-caret-down-square-fill'); 55 | }); 56 | } 57 | } 58 | 59 | window.addEventListener('DOMContentLoaded', () => { 60 | window.Theme = new Navigation(); 61 | }); -------------------------------------------------------------------------------- /default/admin/users/edit/account.php: -------------------------------------------------------------------------------- 1 | 12 |

13 |
14 |
15 | 16 |
17 | 18 |
19 |
20 |
21 | 22 |
23 | 24 |
25 |
26 |
27 | 28 |
29 | 30 |
31 |
32 |
33 |
34 | 35 |
36 |
37 |
38 | 39 |
40 |
41 | 42 |
43 |
44 |
45 |
46 |
47 | 48 |
49 | 50 |
51 |
52 |
53 | 54 |
55 | 56 |
57 |
58 |
59 | 60 |
61 |
-------------------------------------------------------------------------------- /default/js/components/ajax.js: -------------------------------------------------------------------------------- 1 | /** 2 | * fruithost | OpenSource Hosting 3 | * 4 | * @author Adrian Preuß 5 | * @version 1.0.0 6 | * @license MIT 7 | */ 8 | 'use strict'; 9 | 10 | class AjaxComponent { 11 | constructor() { 12 | [].map.call(document.querySelectorAll('.ajax'), (form) => { 13 | /* 14 | form.addEventListener('click', (event) => { 15 | this.submit.apply(form, [ event ]); 16 | }); 17 | */ 18 | 19 | form.addEventListener('submit', (event) => { 20 | this.submit.apply(form, [ event ]); 21 | }); 22 | }); 23 | } 24 | serialize(form) { 25 | let result = {}; 26 | let data = new FormData(form); 27 | 28 | for(let key of data.keys()) { 29 | result[key] = data.get(key); 30 | } 31 | 32 | return result; 33 | } 34 | 35 | submit(event) { 36 | event.preventDefault(); 37 | 38 | try { 39 | let form = this; 40 | // @ToDo add onError? 41 | 42 | new Ajax(form.action).onSuccess(function (response) { 43 | if(response.toLowerCase() === 'true' || response.toLowerCase() === '1') { 44 | window.location.reload(); 45 | return; 46 | 47 | } else if(response.toLowerCase() == 'close') { 48 | let node = form.parentNode.closest('.modal'); 49 | node.classList.remove('fade'); 50 | let modal = bootstrap.Modal.getInstance(node); 51 | modal.hide(); 52 | event.stopPropagation(); 53 | return; 54 | 55 | } else if(response.toLowerCase() === 'false') { 56 | response = 'An unknown error has occurred.'; // @ToDo Language 57 | } 58 | 59 | let content = form.querySelector('.modal-body'); 60 | let alert = content.querySelector('.alert'); 61 | 62 | if (alert) { 63 | content.removeChild(alert); 64 | } 65 | 66 | alert = document.createElement('div'); 67 | alert.classList.add('alert'); 68 | alert.classList.add('alert-danger'); 69 | alert.setAttribute('role', 'alert'); 70 | alert.innerHTML = response; 71 | content.prepend(alert); 72 | }).post(serialize(form)); 73 | } catch (e) { 74 | /* Do Nothing */ 75 | } 76 | 77 | return false; 78 | } 79 | } 80 | 81 | window.addEventListener('DOMContentLoaded', () => { 82 | window.AjaxComponent = new AjaxComponent(); 83 | }); -------------------------------------------------------------------------------- /classes/Hardware/Memory.class.php: -------------------------------------------------------------------------------- 1 | [a-zA-Z]+):([\s]+)(?[0-9]+)\s(?[a-zA-Z]+)$/Uis', $line, $matches); 27 | 28 | if(!empty($matches['name'])) { 29 | $this->memory[trim($matches['name'])] = (int) $matches['value'] * 1024; 30 | } 31 | } 32 | } 33 | 34 | public function getTotal() : ?int { 35 | return $this->memory['MemTotal'] + $this->memory['Cached'] + $this->memory['Buffers']; 36 | } 37 | 38 | public function getUsed() : ?int { 39 | return $this->memory['MemFree']; 40 | } 41 | 42 | public function getSwap() : ?int { 43 | return ($this->memory['SwapTotal'] - $this->memory['SwapFree']); 44 | } 45 | 46 | public function getAssured() : ?int { 47 | return $this->memory['MemAvailable'] + $this->memory['Cached']; 48 | } 49 | 50 | public function getInCache() : ?int { 51 | return ($this->memory['Cached'] + $this->memory['SReclaimable'] - $this->memory['Shmem']) * 10; 52 | } 53 | 54 | public function getPercentage() : ?float { 55 | return (float) number_format(($this->getUsed() / $this->getTotal()) * 100, 2, '.', ','); 56 | } 57 | } 58 | 59 | class Memory { 60 | public static function get() : ?MemoryFactory { 61 | return MemoryFactory::getInstance(); 62 | } 63 | 64 | public static function getUsed() : ?int { 65 | return MemoryFactory::getInstance()->getUsed(); 66 | } 67 | 68 | public static function getTotal() : ?int { 69 | return MemoryFactory::getInstance()->getTotal(); 70 | } 71 | 72 | public static function getSwap() : ?int { 73 | return MemoryFactory::getInstance()->getSwap(); 74 | } 75 | 76 | public static function getAssured() : ?int { 77 | return MemoryFactory::getInstance()->getAssured(); 78 | } 79 | 80 | public static function getInCache() : ?int { 81 | return MemoryFactory::getInstance()->getInCache(); 82 | } 83 | 84 | public static function getPercentage() : ?float { 85 | return MemoryFactory::getInstance()->getPercentage(); 86 | } 87 | } 88 | ?> -------------------------------------------------------------------------------- /libraries/Sepia/PoParser/SourceHandler/StringSource.php: -------------------------------------------------------------------------------- 1 | line = 0; 49 | $this->strings = explode("\n",$string); 50 | $this->total = count($this->strings); 51 | } 52 | 53 | public function getNextLine() 54 | { 55 | if (isset($this->strings[$this->line])) { 56 | $result = $this->strings[$this->line]; 57 | $this->line++; 58 | } else { 59 | $result = false; 60 | } 61 | return $result; 62 | } 63 | 64 | public function ended() 65 | { 66 | return ($this->line>=$this->total); 67 | } 68 | 69 | public function close() 70 | { 71 | $this->line = 0; 72 | } 73 | 74 | public function save($ignore) 75 | { 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /classes/Hardware/NetworkFlag.enum.php: -------------------------------------------------------------------------------- 1 | value){ 70 | return $status; 71 | } 72 | } 73 | 74 | throw new \ValueError("$name is not a valid backing value for enum " . self::class); 75 | } 76 | 77 | public static function for(NetworkFlag $name) : string { 78 | foreach(self::cases() as $status) { 79 | if($status->name === $name->name) { 80 | return $status->name; 81 | } 82 | } 83 | 84 | throw new \ValueError("$name is not a valid backing value for enum " . self::class); 85 | } 86 | 87 | public static function tryFromName(string $name) : NetworkFlag | null { 88 | try { 89 | return self::fromName($name); 90 | } catch (\ValueError $error) { 91 | return null; 92 | } 93 | } 94 | } 95 | ?> -------------------------------------------------------------------------------- /classes/Hardware/NetworkInterface.class.php: -------------------------------------------------------------------------------- 1 | id = $id; 26 | 27 | switch($this->id) { 28 | case 'sit0': // Tunnel-Device for IPv6 in IPv4 29 | $this->tunnel = true; 30 | $this->virtual = true; 31 | break; 32 | case 'teql0': 33 | $this->virtual = true; 34 | break; 35 | } 36 | } 37 | 38 | public function getID() { 39 | return $this->id; 40 | } 41 | 42 | public function isVirtual() : bool { 43 | return $this->virtual; 44 | } 45 | 46 | public function isTunnel() : bool { 47 | return $this->tunnel; 48 | } 49 | 50 | public function getState() : NetworkState { 51 | return $this->state; 52 | } 53 | public function setState(NetworkState $state) { 54 | $this->state = $state; 55 | } 56 | 57 | public function getType() { 58 | return $this->type; 59 | } 60 | public function setType($type) { 61 | $this->type = $type; 62 | } 63 | 64 | public function addFlag(string $flag) : void { 65 | $this->flags[] = NetworkFlag::tryFromName($flag); 66 | } 67 | 68 | public function getFlags() : array { 69 | return $this->flags; 70 | } 71 | 72 | public function hasFlag(string $flag) : bool { 73 | return in_array(NetworkFlag::tryFromName($flag), $this->flags); 74 | } 75 | 76 | public function setAddress($address) { 77 | $this->address = $address; 78 | } 79 | 80 | public function getAddress() { 81 | return $this->address; 82 | } 83 | 84 | public function addAddress(NetworkAddress $address) { 85 | $this->addresses[] = $address; 86 | } 87 | 88 | public function getAddresses() : array { 89 | return $this->addresses; 90 | } 91 | 92 | public function hasAddresses() : bool { 93 | return !empty($this->addresses); 94 | } 95 | 96 | public function setBroadcast($broadcast) { 97 | $this->broadcast = $broadcast; 98 | } 99 | 100 | public function getBroadcast() { 101 | return $this->broadcast; 102 | } 103 | 104 | public function enable() { 105 | shell_exec(sprintf('ifup %s', $this->id)); 106 | } 107 | 108 | public function disable() { 109 | shell_exec(sprintf('ifdown %s', $this->id)); 110 | } 111 | } 112 | ?> 113 | -------------------------------------------------------------------------------- /default/js/components/console.js: -------------------------------------------------------------------------------- 1 | /** 2 | * fruithost | OpenSource Hosting 3 | * 4 | * @author Adrian Preuß 5 | * @version 1.0.0 6 | * @license MIT 7 | */ 8 | 9 | (() => { 10 | 'use strict' 11 | window.addEventListener('DOMContentLoaded', () => { 12 | document.body.style.overflow = 'hidden'; 13 | let destination = document.querySelector('input[name="destination"]'); 14 | let output = document.querySelector('div.output'); 15 | let command = document.querySelector('input[name="command"]'); 16 | let parser = new Terminal(); 17 | 18 | function send(cmd) { 19 | new Ajax(destination.value).onError(function (event, error, response) { 20 | console.warn(event, error, response); 21 | }).onSuccess(function (response) { 22 | if (response === '\u001B[H\u001B[2J\u001B[3J' || response === 'clear') { 23 | output.innerHTML = ''; 24 | return; 25 | } 26 | 27 | output.innerHTML += parser.parse(response); 28 | command.focus(); 29 | output.scrollTo(0, output.scrollHeight); 30 | }).post({ 31 | action: 'command', 32 | command: cmd 33 | }); 34 | } 35 | 36 | let history = []; 37 | let position = 0; 38 | 39 | command.addEventListener('keydown', (event) => { 40 | switch (event.keyCode) { 41 | /* Enter */ 42 | case 13: 43 | let text = command.value; 44 | history.push(text); 45 | position = history.length; 46 | send(text); 47 | command.value = ''; 48 | break; 49 | 50 | /* Up */ 51 | case 38: 52 | if (position > 0) { 53 | --position; 54 | 55 | if (position < 0) { 56 | position = history.length - 1; 57 | } 58 | 59 | command.blur(); 60 | command.value = history[position]; 61 | command.focus(); 62 | } 63 | break; 64 | 65 | /* Down */ 66 | case 40: 67 | if (position >= history.length) { 68 | break; 69 | } 70 | 71 | position++; 72 | 73 | if (position === history.length) { 74 | command.value = ''; 75 | } else { 76 | command.blur(); 77 | command.focus(); 78 | command.value = history[position]; 79 | } 80 | break; 81 | } 82 | }); 83 | 84 | send('motd'); 85 | command.focus(); 86 | }); 87 | })(); -------------------------------------------------------------------------------- /default/admin/users/list.php: -------------------------------------------------------------------------------- 1 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | fetch($u->id); 29 | 30 | if($user->getID() == null) { 31 | continue; 32 | } 33 | ?> 34 | 35 | 41 | 44 | 48 | 51 | 54 | 67 | 68 | 71 | 72 |
36 |
37 | getID() == 1 ? ' DISABLED' : ''); ?> /> 38 | 39 |
40 |
42 | 43 | 45 | #getID(); ?> 46 | getUsername(); ?> 47 | 49 | getFullName(); ?> 50 | 52 | getMail(); ?> 53 | 55 |
56 | %s', $this->url('/admin/users/'.$user->getID()), I18N::get('Edit')); 59 | } 60 | 61 | if(Auth::hasPermission('USERS::DELETE') && $user->getID() != 1) { 62 | printf('', I18N::get('Do you really wan\'t to delete the user?'), I18N::get('Delete')); 63 | } 64 | ?> 65 |
66 |
73 |
-------------------------------------------------------------------------------- /classes/System/Update.class.php: -------------------------------------------------------------------------------- 1 | getSettings('UPDATE_ENABLED', true)) { 23 | $time = self::$core->getSettings('UPDATE_TIME', null); 24 | 25 | if($time == null) { 26 | self::run(); 27 | } else { 28 | $last = new \DateTime($time); 29 | $last->add(\DateInterval::createFromDateString('15 minutes')); 30 | 31 | if(new \DateTime() > $last) { 32 | self::run(); 33 | } 34 | } 35 | } 36 | } 37 | 38 | public static function run() { 39 | $license = self::getLicense(); 40 | $result = self::get('getVersion', [ 41 | 'license' => $license, 42 | 'host' => $_SERVER['SERVER_NAME'], 43 | 'ip' => $_SERVER['SERVER_ADDR'], 44 | 'admin' => $_SERVER['SERVER_ADMIN'], 45 | 'ssl' => isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' 46 | ]); 47 | 48 | /* Refresh License */ 49 | if(!$result->status && $result->error == 'LICENSE_PROBLEM') { 50 | self::$core->removeSettings('UPDATE_LICENSE'); 51 | self::check(); 52 | return; 53 | } 54 | 55 | self::setOnlineVersion($result->version); 56 | } 57 | 58 | protected static function get($action, $data) { 59 | $request = curl_init(sprintf('https://%s/', UPDATE_ENDPOINT)); 60 | curl_setopt($request, CURLOPT_RETURNTRANSFER, true); 61 | curl_setopt($request, CURLOPT_POSTFIELDS, array_merge([ 62 | 'action' => $action 63 | ], $data)); 64 | $response = curl_exec($request); 65 | curl_close($request); 66 | 67 | try { 68 | $json = json_decode($response); 69 | 70 | if($json != NULL) { 71 | $response = $json; 72 | } 73 | } catch(\Exception $e) { 74 | 75 | } 76 | 77 | return $response; 78 | } 79 | 80 | protected static function setOnlineVersion($version) : void { 81 | self::$core->setSettings('UPDATE_TIME', date('Y-m-d H:i:s', time())); 82 | self::$core->setSettings('UPDATE_VERSION', $version); 83 | } 84 | 85 | public static function getLicense() : string { 86 | if(self::$core->hasSettings('UPDATE_LICENSE')) { 87 | return self::$core->getSettings('UPDATE_LICENSE', null); 88 | } else { 89 | $result = self::get('getLicense', [ 90 | 'host' => $_SERVER['SERVER_NAME'], 91 | 'ip' => $_SERVER['SERVER_ADDR'], 92 | 'admin' => $_SERVER['SERVER_ADMIN'], 93 | 'ssl' => isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' 94 | ]); 95 | 96 | self::$core->setSettings('UPDATE_LICENSE', $result->license); 97 | 98 | return $result->license; 99 | } 100 | } 101 | } 102 | ?> -------------------------------------------------------------------------------- /default/admin/users/edit.php: -------------------------------------------------------------------------------- 1 | header(); 13 | ?> 14 | 20 | 23 | 24 | 29 | 30 | 33 |
34 |
35 | display('admin/users/edit/account', [ 'user' => $user ]); 37 | ?> 38 |
39 |
40 | display('admin/users/edit/password', [ 'user' => $user, 'timezones' => $timezones ]); 42 | ?> 43 |
44 |
45 | display('admin/users/edit/settings', [ 'user' => $user ]); 47 | ?> 48 |
49 |
50 | display('admin/users/edit/permissions', [ 'user' => $user ]); 52 | ?> 53 |
54 |
55 | footer(); 57 | ?> -------------------------------------------------------------------------------- /default/admin/themes.php: -------------------------------------------------------------------------------- 1 | header(); 14 | 15 | if(!Auth::hasPermission('THEMES::VIEW')) { 16 | ?> 17 | 21 | footer(); 23 | exit(); 24 | } 25 | ?> 26 |
27 | 42 | 51 | 54 | 55 | 58 | 59 | 62 |
63 |
64 | display('admin/themes/empty'); 68 | break; 69 | } 70 | ?> 71 |
72 |
73 |
74 | footer(); 76 | ?> -------------------------------------------------------------------------------- /handler/server/logs.php: -------------------------------------------------------------------------------- 1 | assign('error', I18N::get('You have no permissions for this action!')); 68 | exit(); 69 | } 70 | 71 | if(!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') { 72 | Response::addHeader('Content-Type', 'text/plain; charset=UTF-8'); 73 | 74 | switch($_POST['action']) { 75 | case 'refresh': 76 | print json_encode($list); 77 | break; 78 | case 'file': 79 | $file = $_POST['file']; 80 | $content = null; 81 | if(!file_exists($file)) { 82 | $content = null; 83 | } 84 | if(!is_readable($file)) { 85 | $content = false; 86 | } 87 | if(!empty($file)) { 88 | try { 89 | $content = @file_get_contents($file); 90 | } catch(\Exception $e) { 91 | $content = false; 92 | } 93 | } 94 | if($content === false) { 95 | $content = shell_exec(sprintf('cat %s 2>&1', $file)); 96 | if(str_starts_with($content, 'cat:') && str_contains($content, 'Permission denied')) { 97 | $content = false; 98 | } 99 | } 100 | print json_encode([ 101 | 'file' => $file, 102 | 'content' => $content 103 | ]); 104 | break; 105 | } 106 | 107 | exit(); 108 | } 109 | } 110 | 111 | $template->assign('list', $list); 112 | $template->getFiles()->addJavascript('terminal', $this->url('js/terminal.js'), '1.0.0'); 113 | ?> -------------------------------------------------------------------------------- /default/js/ajax.js: -------------------------------------------------------------------------------- 1 | /** 2 | * fruithost | OpenSource Hosting 3 | * 4 | * @author Adrian Preuß 5 | * @version 1.0.0 6 | * @license MIT 7 | */ 8 | 9 | const AjaxState = { 10 | NOT_INITALIZED: 0, 11 | CONNECTION_ESTABLISHED: 1, 12 | REQUEST_RECEIVED: 2, 13 | PROCESSING: 3, 14 | FINISHED: 4 15 | }; 16 | 17 | const Ajax = function () { 18 | let socket; 19 | let url; 20 | let callbacks = { 21 | success: [], 22 | error: [] 23 | }; 24 | 25 | this.__construct = function __construct(url) { 26 | this.socket = new XMLHttpRequest(); 27 | this.url = url; 28 | this.socket.onreadystatechange = this.handle.bind(this); 29 | }; 30 | 31 | this.handle = function handle(event) { 32 | switch (this.socket.readyState) { 33 | case AjaxState.NOT_INITALIZED: 34 | case AjaxState.CONNECTION_ESTABLISHED: 35 | case AjaxState.REQUEST_RECEIVED: 36 | case AjaxState.PROCESSING: 37 | /* Do currently Nohting */ 38 | break; 39 | case AjaxState.FINISHED: 40 | if (this.socket.readyState == 4 && this.socket.status == 200) { 41 | try { 42 | let json = JSON.parse(event.target.responseText); 43 | 44 | callbacks.success.forEach(function onSuccessCallback(callback) { 45 | callback.apply(this, [json]); 46 | }.bind(this)); 47 | } catch (exception) { 48 | callbacks.success.forEach(function onSuccessCallback(callback) { 49 | callback.apply(this, [event.target.responseText]); 50 | }.bind(this)); 51 | } 52 | } else { 53 | callbacks.error.forEach(function onErrorCallback(callback) { 54 | callback.apply(this, [event, 'Error', event.target.responseText]); 55 | }.bind(this)); 56 | } 57 | break; 58 | } 59 | }; 60 | 61 | this.setHeaders = function setHeaders() { 62 | this.socket.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); 63 | this.socket.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); 64 | }; 65 | 66 | this.onError = function onError(callback) { 67 | callbacks.success.push(callback); 68 | 69 | return this; 70 | }; 71 | 72 | this.onSuccess = function onSuccess(callback) { 73 | callbacks.success.push(callback); 74 | 75 | return this; 76 | }; 77 | 78 | this.post = function post(data) { 79 | this.socket.open('POST', this.url, true); 80 | this.setHeaders(); 81 | 82 | this.socket.send((typeof (data) == 'string') ? data : Object.keys(data).map(function (key) { 83 | return encodeURIComponent(key) + '=' + encodeURIComponent(data[key]) 84 | }).join('&')); 85 | 86 | return this; 87 | }; 88 | 89 | this.get = function get() { 90 | this.socket.open('GET', this.url, true); 91 | this.setHeaders(); 92 | this.socket.send(); 93 | 94 | return this; 95 | }; 96 | 97 | this.__construct.apply(this, arguments); 98 | }; -------------------------------------------------------------------------------- /classes/Storage/PropertiesCache.class.php: -------------------------------------------------------------------------------- 1 | cache[$name])) { 17 | return true; 18 | } 19 | 20 | return Database::exists(sprintf('SELECT `id` FROM `%s%s` WHERE `key`=:key LIMIT 1', DATABASE_PREFIX, $table), [ 21 | 'key' => $name 22 | ]); 23 | } 24 | 25 | public function remove(string $table, string $name) : void { 26 | if(isset($this->cache[$name])) { 27 | unset($this->cache[$name]); 28 | } 29 | 30 | Database::delete(DATABASE_PREFIX . $table, [ 31 | 'key' => $name 32 | ]); 33 | } 34 | 35 | public function get(string $table, ?string $name = null, mixed $default = null) : mixed { 36 | /* Get all Settings */ 37 | if($name == null) { 38 | $data = Database::fetch(sprintf('SELECT * FROM `%s%s`', DATABASE_PREFIX, $table)); 39 | 40 | foreach($data AS $index => $entry) { 41 | // Is NULL-Value 42 | if($entry->value == null) { 43 | $entry->value = 'NULL'; 44 | 45 | // Is Boolean: False 46 | } else if(in_array(strtolower($entry->value), [ 47 | 'off', 'false', 'no' 48 | ])) { 49 | $entry->value = false; 50 | 51 | // Is Boolean: True 52 | } else if(in_array(strtolower($entry->value), [ 53 | 'on', 'true', 'yes' 54 | ])) { 55 | $entry->value = true; 56 | } 57 | } 58 | 59 | return $data; 60 | } 61 | 62 | if(isset($this->cache[$name])) { 63 | return $this->cache[$name]; 64 | } 65 | 66 | $result = Database::single(sprintf('SELECT * FROM `%s%s` WHERE `key`=:key LIMIT 1', DATABASE_PREFIX, $table), [ 67 | 'key' => $name 68 | ]); 69 | 70 | if(!empty($result) && !empty($result->value)) { 71 | // Is Boolean: False 72 | if(in_array(strtolower($result->value), [ 73 | 'off', 'false', 'no' 74 | ])) { 75 | $this->cache[$name] = false; 76 | return $this->cache[$name]; 77 | 78 | // Is Boolean: True 79 | } else if(in_array(strtolower($result->value), [ 80 | 'on', 'true', 'yes' 81 | ])) { 82 | $this->cache[$name] = true; 83 | return $this->cache[$name]; 84 | } 85 | 86 | $this->cache[$name] = $result->value; 87 | return $this->cache[$name]; 88 | } 89 | 90 | return $default; 91 | } 92 | 93 | public function set(string $table, string $name, mixed $value) : void { 94 | if(is_bool($value)) { 95 | $value = ($value ? 'true' : 'false'); 96 | } 97 | 98 | if(Database::exists(sprintf('SELECT `id` FROM `%s%s` WHERE `key`=:key LIMIT 1', DATABASE_PREFIX, $table), [ 99 | 'key' => $name 100 | ])) { 101 | Database::update(DATABASE_PREFIX . $table, [ 'key' ], [ 102 | 'key' => $name, 103 | 'value' => $value 104 | ]); 105 | } else { 106 | Database::insert(DATABASE_PREFIX . $table, [ 107 | 'id' => null, 108 | 'key' => $name, 109 | 'value' => $value 110 | ]); 111 | } 112 | 113 | $this->cache[$name] = $value; 114 | } 115 | } 116 | ?> -------------------------------------------------------------------------------- /default/modal.php: -------------------------------------------------------------------------------- 1 | 0) { 11 | foreach($modals AS $modal) { 12 | ?> 13 | 53 | isShowing()) { 55 | ?> 56 | 61 | -------------------------------------------------------------------------------- /classes/Hardware/NetworkInterfaces.class.php: -------------------------------------------------------------------------------- 1 | devices[$entry] = new NetworkInterface($entry); 29 | } 30 | 31 | $result = shell_exec('ip --json address show'); 32 | 33 | foreach(json_decode($result) AS $entry) { 34 | $device = $this->devices[$entry->ifname]; 35 | 36 | if(isset($entry->link_type)) { 37 | $device->setType($entry->link_type); 38 | } 39 | 40 | if(isset($entry->operstate)) { 41 | $device->setState(NetworkState::tryFromName($entry->operstate)); 42 | } 43 | 44 | if(isset($entry->address)) { 45 | $device->setAddress($entry->address); 46 | } 47 | 48 | if(isset($entry->broadcast)) { 49 | $device->setBroadcast($entry->broadcast); 50 | } 51 | 52 | if(isset($entry->flags)) { 53 | foreach($entry->flags AS $flag) { 54 | $device->addFlag($flag); 55 | } 56 | } 57 | 58 | if(isset($entry->addr_info)) { 59 | foreach($entry->addr_info AS $address) { 60 | $device->addAddress(new NetworkAddress($address)); 61 | } 62 | } 63 | } 64 | } 65 | 66 | public function getDevices() : array { 67 | return $this->devices; 68 | } 69 | 70 | public function hasDevices() : bool { 71 | return !empty($this->devices); 72 | } 73 | 74 | public function getHostname() : string { 75 | return trim(shell_exec('hostname')); 76 | } 77 | 78 | public function getPanelHostname() : string { 79 | return trim($_SERVER['HTTP_HOST']); 80 | } 81 | 82 | public function getIPAddress() : string { 83 | return trim($_SERVER['SERVER_ADDR']); 84 | } 85 | 86 | public function getDevice($id) : NetworkInterface | null { 87 | return $this->devices[$id]; 88 | } 89 | } 90 | 91 | class NetworkInterfaces { 92 | public static function get() : ?NetworkInterfacesFactory { 93 | return NetworkInterfacesFactory::getInstance(); 94 | } 95 | 96 | public static function getDevices() : array { 97 | return NetworkInterfacesFactory::getInstance()->getDevices(); 98 | } 99 | 100 | public static function getHostname() : string { 101 | return NetworkInterfacesFactory::getInstance()->getHostname(); 102 | } 103 | 104 | public static function getPanelHostname() : string { 105 | return NetworkInterfacesFactory::getInstance()->getPanelHostname(); 106 | } 107 | 108 | public static function getIPAddress() : string { 109 | return NetworkInterfacesFactory::getInstance()->getIPAddress(); 110 | } 111 | 112 | public static function getDevice($id) : NetworkInterface | null { 113 | return NetworkInterfacesFactory::getInstance()->getDevice($id); 114 | } 115 | } 116 | ?> -------------------------------------------------------------------------------- /handler/server/server.php: -------------------------------------------------------------------------------- 1 | assign('error', I18N::get('You have no permissions for this action!')); 23 | exit(); 24 | } 25 | 26 | Response::addHeader('Content-Type', 'text/plain; charset=UTF-8'); 27 | 28 | switch($_POST['command']) { 29 | case 'get_live_usage': 30 | print json_encode([ 31 | 'total' => Utils::getFileSize(Memory::getTotal()), 32 | 'used' => Utils::getFileSize(Memory::getUsed()), 33 | 'cache' => Utils::getFileSize(Memory::getInCache()), 34 | 'assured' => Utils::getFileSize(Memory::getAssured()), 35 | 'swap' => Utils::getFileSize(Memory::getSwap()), 36 | 'percentage' => Memory::getPercentage() 37 | ]); 38 | break; 39 | } 40 | 41 | exit(); 42 | } 43 | 44 | $time_format = Auth::getSettings('TIME_FORMAT', null, 'd.m.Y - H:i'); 45 | $from = $this->getCore()->getSettings('UPDATE_TIME', null); 46 | $to = $this->getCore()->getSettings('DAEMON_TIME_END', null); 47 | $status = null; 48 | 49 | if($from !== null) { 50 | $time_update = new \DateTime($from); 51 | } 52 | 53 | if($to !== null) { 54 | $time_status = new \DateTime($to); 55 | } 56 | 57 | if($to !== null && $from !== null) { 58 | $time_status->add(\DateInterval::createFromDateString('15 minutes')); 59 | $template->assign('time_update', ($time_update->format($time_format))); 60 | $status = $time_update < new \DateTime(); 61 | } else { 62 | $template->assign('time_update', null); 63 | } 64 | 65 | $template->assign('update_version', $this->getCore()->getSettings('UPDATE_VERSION')); 66 | $template->assign('update_license', Update::getLicense()); 67 | $template->assign('time_php', date($time_format)); 68 | $template->assign('time_system', OperatingSystem::getTime($time_format)); 69 | $template->assign('os', OperatingSystem::getPrettyName()); 70 | $template->assign('id', OperatingSystem::getID()); 71 | $template->assign('kernel', OperatingSystem::getKernel()); 72 | $template->assign('machine_type', OperatingSystem::getMachineType()); 73 | $template->assign('uptime', OperatingSystem::getUptime(true)); 74 | $template->assign('memory', Memory::get()); 75 | $template->assign('network', NetworkInterfaces::get()); 76 | $template->assign('disks', PhysicalDrives::getDevices()); 77 | $template->assign('version', file_get_contents('.version')); 78 | $template->assign('daemon', [ 79 | 'status' => $status, 80 | 'started' => strtotime($this->getCore()->getSettings('DAEMON_TIME_START', 0)), 81 | 'start' => date($time_format, strtotime($this->getCore()->getSettings('DAEMON_TIME_START', 0))), 82 | 'end' => date($time_format, strtotime($this->getCore()->getSettings('DAEMON_TIME_END', 0))), 83 | 'ended' => strtotime($this->getCore()->getSettings('DAEMON_TIME_END', 0)), 84 | 'time' => number_format($this->getCore()->getSettings('DAEMON_RUNNING_END', 0) - $this->getCore()->getSettings('DAEMON_RUNNING_START', 0), 4, ',', '.') 85 | ]); 86 | 87 | $template->getFiles()->addJavascript('statistics', $this->url('js/statistics.js'), '1.0.0', [ 'ajax' ], TemplateFiles::FOOTER); 88 | ?> 89 | -------------------------------------------------------------------------------- /classes/Network/Router.class.php: -------------------------------------------------------------------------------- 1 | core = $core; 22 | } 23 | 24 | public function addRoute(string $name, callable $callback) : Route | null { 25 | $route = new Route(); 26 | $this->routes[$name] = $route; 27 | $route->setPath($name); 28 | $route->setCallback($callback); 29 | 30 | return $route; 31 | } 32 | 33 | public function cleanUp() : void { 34 | $this->routes = []; 35 | } 36 | 37 | public function routeExists(string $name) : bool { 38 | foreach($this->routes AS $route) { 39 | if(preg_match('/(\(|\)|\[|\])/Uis', $route->getPath()) && preg_match('#' . $route->getPath() . '#Uis', $name)) { 40 | return true; 41 | } 42 | } 43 | 44 | return array_key_exists($name, $this->routes); 45 | } 46 | 47 | public function executeRoute(string $name) : void { 48 | $this->current = $name; 49 | 50 | foreach($this->routes AS $route) { 51 | if(preg_match('/(\(|\)|\[|\])/Uis', $route->getPath())) { 52 | if(preg_match('#' . $route->getPath() . '#Uis', $name, $matches)) { 53 | array_shift($matches); 54 | call_user_func_array($route->getCallback(), $matches); 55 | return; 56 | } 57 | } 58 | } 59 | 60 | $route = $this->routes[$name]; 61 | $callback = $route->getCallback(); 62 | $callback(); 63 | } 64 | 65 | public function getCurrent() : ?string { 66 | return $this->current; 67 | } 68 | 69 | public function is(string $name) : bool { 70 | return (strtolower($this->getCurrent()) === strtolower($name)); 71 | } 72 | 73 | public function startsWith(string $name) : bool { 74 | return (str_starts_with(strtolower($this->getCurrent()), strtolower($name))); 75 | } 76 | 77 | public function redirectTo(string $redirect) : void { 78 | $this->redirect = $redirect; 79 | } 80 | 81 | public function run(bool $ajax = false) : void { 82 | // @ToDo is it secure? 83 | if($ajax) { 84 | $data = preg_replace('#(http|https)://([^/]+)#', '', $_SERVER['HTTP_REFERER']); 85 | } else if(isset($_SERVER['REQUEST_URI'])) { 86 | $data = $_SERVER['REQUEST_URI']; 87 | } else { 88 | $data = null; 89 | } 90 | 91 | if(empty($data)) { 92 | return; 93 | } 94 | 95 | $uri = explode('/', $data); 96 | $name = explode('/', (isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : '/')); 97 | 98 | for($i = 0; $i < sizeof($name); $i++) { 99 | if($uri[$i] === $name[$i]) { 100 | unset($uri[$i]); 101 | } 102 | } 103 | 104 | $command = array_values($uri); 105 | $route = ''; 106 | 107 | foreach($command AS $index => $directory) { 108 | $route .= '/' . $directory; 109 | } 110 | 111 | // Remove "GET" 112 | if(str_contains($route, "?")) { 113 | $split = explode('?', $route); 114 | $route = $split[0]; 115 | } 116 | 117 | // Remove "ajax" 118 | if($ajax) { 119 | $route = str_replace('/ajax', '', $route); 120 | } 121 | 122 | Response::header(); 123 | 124 | if($this->routeExists($route)) { 125 | $this->executeRoute($route); 126 | } else if(empty($this->redirect)) { 127 | $this->core->getTemplate()->display('error/404'); 128 | } else { 129 | Response::redirect($this->redirect); 130 | } 131 | } 132 | } 133 | ?> -------------------------------------------------------------------------------- /classes/UI/Icon.class.php: -------------------------------------------------------------------------------- 1 | getTemplate()->getTheme()); 29 | $content = null; 30 | 31 | if(file_exists($path) && is_readable($path)) { 32 | $content = file_get_contents($path); 33 | } else { 34 | $path = sprintf('%1$s%2$sdefault%2$sicons.json', PATH, DS); 35 | 36 | if(file_exists($path) && is_readable($path)) { 37 | $content = file_get_contents($path); 38 | } 39 | } 40 | 41 | if(empty($content)) { 42 | throw new \Exception('Can\'t load icon definitions: ' . $path); 43 | } 44 | 45 | $json = json_decode($content, false); 46 | 47 | if(json_last_error() !== JSON_ERROR_NONE) { 48 | throw new \Exception('Can\'t parse icon JSON definitions: ' . $path); 49 | } 50 | 51 | self::$definition = $json; 52 | } 53 | 54 | public static function render(string $icon, ?array $options = []) : string { 55 | $json = self::$definition; 56 | $icons = $json->definitions; 57 | $template = $json->template; 58 | $classes = empty($template->classes) ? [] : array_merge([], $template->classes); 59 | $attributes = empty($template->attributes) ? [] : array_merge([], $template->attributes); 60 | $attr = []; 61 | 62 | if(!empty($options['classes'])) { 63 | $classes = array_merge($classes, $options['classes']); 64 | } 65 | 66 | if(!empty($options['attributes'])) { 67 | $attributes = array_merge($attributes, $options['attributes']); 68 | } 69 | 70 | foreach($attributes AS $name => $value) { 71 | if(is_bool($value)) { 72 | $value = $value ? 'true' : 'false'; 73 | } 74 | 75 | $attr[] = sprintf('%s="%s"', $name, $value); 76 | } 77 | 78 | if(!empty($icons->{$icon})) { 79 | switch($template->content->type) { 80 | case 'class': 81 | $classes[] = sprintf($template->content->html, $icons->{$icon}); 82 | break; 83 | default: 84 | throw new \Exception('Unknown icon definition: ' . $template->content->type); 85 | break; 86 | } 87 | 88 | return sprintf(self::$core->getHooks()->applyFilter('icons_html', '<%1$s class="%2$s" %3$s>'), $template->element, implode(' ', $classes), implode(' ', $attr)); 89 | } 90 | 91 | return sprintf('[Icon %s]', $icon); 92 | } 93 | 94 | public static function get(string $icon) : string { 95 | $json = self::$definition; 96 | $icons = $json->definitions; 97 | $template = $json->template; 98 | $class = []; 99 | 100 | if(!empty($icons->{$icon})) { 101 | switch($template->content->type) { 102 | case 'class': 103 | $class = sprintf($template->content->html, $icons->{$icon}); 104 | break; 105 | default: 106 | throw new \Exception('Unknown icon definition: ' . $template->content->type); 107 | break; 108 | } 109 | 110 | return $class; 111 | } 112 | 113 | return sprintf('[Icon %s]', $icon); 114 | } 115 | 116 | public static function show(string $name, ?array $options = []) : void { 117 | print self::render($name, $options); 118 | } 119 | } 120 | ?> -------------------------------------------------------------------------------- /default/server/packages.php: -------------------------------------------------------------------------------- 1 | header(); 14 | if(!Auth::hasPermission('SERVER::PACKAGES')) { 15 | ?> 16 | 20 | footer(); 22 | exit(); 23 | } 24 | 25 | $result = shell_exec('dpkg-query -W -f=\'${binary:Package};${Version};${binary:Summary};${Maintainer}\n\''); 26 | $lines = (empty($result) ? [] : explode(PHP_EOL, $result)); 27 | // @ToDo group by names (for sample php-*) 28 | ?> 29 |
30 | 40 | 41 |
42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 53 | 54 | 55 | 56 | 69 | 70 | 74 | 75 | 76 | 79 | 80 |
71 | 72 |

73 |
81 |
82 |
83 | footer(); 85 | ?> -------------------------------------------------------------------------------- /default/admin/modules/info.php: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /classes/Localization/I18N.class.php: -------------------------------------------------------------------------------- 1 | 'English' 23 | ]; 24 | 25 | public function __construct() { 26 | self::loadLanguages(); 27 | self::load(self::set()); 28 | } 29 | 30 | public static function addPath($path) : void { 31 | $file = sprintf('%s%s.po', $path, self::set()); 32 | 33 | if(file_exists($file)) { 34 | $language = new Parser(new FileSystem($file)); 35 | self::$translation = $language->parse(self::$translation); 36 | } 37 | } 38 | 39 | public static function reload() : void { 40 | self::load(self::set()); 41 | } 42 | 43 | /* @ToDo Remove, its core-method, but must be added for the Daemon. Check if we can remove these now */ 44 | protected static function getSettings(string $name, mixed $default = null) : mixed { 45 | $result = Database::single('SELECT * FROM `' . DATABASE_PREFIX . 'settings` WHERE `key`=:key LIMIT 1', [ 46 | 'key' => $name 47 | ]); 48 | 49 | if(!empty($result)) { 50 | // Is Boolean: False 51 | if(in_array(strtolower($result->value), [ 52 | 'off', 'false', 'no' 53 | ])) { 54 | return false; 55 | // Is Boolean: True 56 | } else if(in_array(strtolower($result->value), [ 57 | 'on', 'true', 'yes' 58 | ])) { 59 | return true; 60 | } else if(!empty($result->value)) { 61 | return $result->value; 62 | } 63 | } 64 | 65 | return $default; 66 | } 67 | 68 | public static function set() : string { 69 | $language = Auth::getSettings('LANGUAGE', null, self::getSettings('LANGUAGE', 'en_US')); 70 | 71 | if(!Auth::isLoggedIn() && Request::has('lang')) { 72 | $language = Request::get('lang'); 73 | } 74 | 75 | return $language; 76 | } 77 | 78 | protected static function load(string $language) : void { 79 | $file = sprintf('%slanguages/%s.po', PATH, $language); 80 | 81 | if(file_exists($file)) { 82 | $language = new Parser(new FileSystem($file)); 83 | self::$translation = $language->parse(); 84 | } else { 85 | self::$translation = null; 86 | } 87 | } 88 | 89 | protected static function loadLanguages() : void { 90 | foreach(new \DirectoryIterator(sprintf('%slanguages/', PATH)) AS $info) { 91 | if($info->isDot()) { 92 | continue; 93 | } 94 | 95 | if(preg_match('/(.*)\.po$/Uis', $info->getFileName(), $matches)) { 96 | $language = new Parser(new FileSystem($info->getPathName())); 97 | $parsed = $language->parse(); 98 | $header = $parsed->getHeader(); 99 | 100 | foreach($header->asArray() AS $line) { 101 | if(preg_match('/Language: (.*)$/Uis', $line, $names)) { 102 | self::$languages[$matches[1]] = $names[1]; 103 | break; 104 | } 105 | } 106 | } 107 | } 108 | } 109 | 110 | public static function getLanguages() : array { 111 | return self::$languages; 112 | } 113 | 114 | public static function __($string) : void { 115 | print self::get($string); 116 | } 117 | 118 | public static function get($string) : string { 119 | if(self::$translation == null) { 120 | return $string; 121 | } 122 | 123 | $table = self::$translation->getEntry($string); 124 | 125 | if($table == null) { 126 | return $string; 127 | } 128 | 129 | if(empty($table->getMsgStr())) { 130 | return $string; 131 | } 132 | 133 | return $table->getMsgStr(); 134 | } 135 | } 136 | 137 | new I18N(); 138 | ?> -------------------------------------------------------------------------------- /classes/System/Utils.class.php: -------------------------------------------------------------------------------- 1 | 'application/javascript', 20 | 'json' => 'application/json', 21 | 'xml' => 'application/xml', 22 | 'css' => 'text/css' 23 | ]; 24 | 25 | /* Set MIME-Type when not given */ 26 | if(!$mime) { 27 | if(array_key_exists($extension, $additional)) { 28 | return $additional[$extension]; 29 | } 30 | } 31 | 32 | /* Override misconfigured MIME-Types */ 33 | if(array_key_exists($extension, $additional)) { 34 | if($mime !== $additional[$extension]) { 35 | return $additional[$extension]; 36 | } 37 | } 38 | 39 | return $mime; 40 | } 41 | 42 | public static function randomString(int $length = 10) : string { 43 | $characters = 'abcdefghijklmonpqrstuvwxyz-_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; 44 | $charactersLength = strlen($characters); 45 | $output = ''; 46 | 47 | for($position = 0; $position < $length; $position++) { 48 | // @ToDo Exception handling? 49 | $output .= $characters[random_int(0, $charactersLength - 1)]; 50 | } 51 | 52 | return $output; 53 | } 54 | 55 | public static function zeroize(int $number, int $threshold = 2) : string { 56 | return sprintf('%0' . $threshold . 's', $number); 57 | } 58 | 59 | public static function getTimeDifference(int $from, int $to = null) : string { 60 | if(empty($to)) { 61 | $to = time(); 62 | } 63 | 64 | $diff = (int) abs($to - $from); 65 | $since = ''; 66 | 67 | if($diff < (60 * 60)) { 68 | $mins = round($diff / 60); 69 | 70 | if($mins <= 1) { 71 | $mins = 1; 72 | } 73 | 74 | $since = sprintf(I18N::get('%s mins'), $mins); 75 | } else if($diff < (24 * (60 * 60)) && $diff >= (60 * 60)) { 76 | $hours = round($diff / (60 * 60)); 77 | 78 | if($hours <= 1) { 79 | $hours = 1; 80 | } 81 | 82 | $since = sprintf(I18N::get('%s hours'), $hours); 83 | } else if($diff < (7 * (24 * (60 * 60))) && $diff >= (24 * (60 * 60))) { 84 | $days = round($diff / (24 * (60 * 60))); 85 | 86 | if($days <= 1) { 87 | $days = 1; 88 | } 89 | 90 | $since = sprintf(I18N::get('%s days'), $days); 91 | } else if($diff < (30 * (24 * (60 * 60))) && $diff >= (7 * (24 * (60 * 60)))) { 92 | $weeks = round($diff / (7 * (24 * (60 * 60)))); 93 | 94 | if($weeks <= 1) { 95 | $weeks = 1; 96 | } 97 | 98 | $since = sprintf(I18N::get('%s weeks'), $weeks); 99 | } else if($diff < (365 * (24 * (60 * 60))) && $diff >= (30 * (24 * (60 * 60)))) { 100 | $months = round($diff / (30 * (24 * (60 * 60)))); 101 | 102 | if($months <= 1) { 103 | $months = 1; 104 | } 105 | 106 | $since = sprintf(I18N::get('%s months'), $months); 107 | } else if($diff >= (365 * (24 * (60 * 60)))) { 108 | $years = round($diff / (365 * (24 * (60 * 60)))); 109 | 110 | if($years <= 1) { 111 | $years = 1; 112 | } 113 | 114 | $since = sprintf(I18N::get('%s years'), $years); 115 | } 116 | 117 | return $since; 118 | } 119 | 120 | public static function getFileSize($size) : string { 121 | if($size / 1024000000 > 1) { 122 | $retval = round($size / 1024000000, 1) . ' GB'; 123 | } else if($size / 1024000 > 1) { 124 | $retval = round($size / 1024000, 1) . ' MB'; 125 | } else if($size / 1024 > 1) { 126 | $retval = round($size / 1024, 1) . ' KB'; 127 | } else { 128 | $retval = round($size, 1) . ' bytes'; 129 | } 130 | 131 | return $retval; 132 | } 133 | } 134 | ?> -------------------------------------------------------------------------------- /libraries/Sepia/PoParser/SourceHandler/FileSystem.php: -------------------------------------------------------------------------------- 1 | filePath = $filePath; 46 | $this->fileHandle = null; 47 | } 48 | 49 | /** 50 | * @throws \Exception 51 | */ 52 | protected function openFile() 53 | { 54 | if ($this->fileHandle !== null) { 55 | return; 56 | } 57 | 58 | if (file_exists($this->filePath) === false) { 59 | throw new \Exception('Parser: Input File does not exists: "' . htmlspecialchars($this->filePath) . '"'); 60 | } 61 | 62 | if (is_readable($this->filePath) === false) { 63 | throw new \Exception('Parser: File is not readable: "' . htmlspecialchars($this->filePath) . '"'); 64 | } 65 | 66 | $this->fileHandle = @fopen($this->filePath, 'rb'); 67 | if ($this->fileHandle===false) { 68 | throw new \Exception('Parser: Could not open file: "' . htmlspecialchars($this->filePath) . '"'); 69 | } 70 | } 71 | 72 | /** 73 | * @return bool|string 74 | * @throws \Exception 75 | */ 76 | public function getNextLine() 77 | { 78 | $this->openFile(); 79 | 80 | return fgets($this->fileHandle); 81 | } 82 | 83 | /** 84 | * @return bool 85 | * @throws \Exception 86 | */ 87 | public function ended() 88 | { 89 | $this->openFile(); 90 | 91 | return feof($this->fileHandle); 92 | } 93 | 94 | public function close() 95 | { 96 | if ($this->fileHandle === null) { 97 | return true; 98 | } 99 | 100 | return @fclose($this->fileHandle); 101 | } 102 | 103 | /** 104 | * @param $output 105 | * @param $filePath 106 | * 107 | * @return bool 108 | * @throws \Exception 109 | */ 110 | public function save($output) 111 | { 112 | $result = file_put_contents($this->filePath, $output); 113 | if ($result === false) { 114 | throw new \Exception('Could not write into file '.htmlspecialchars($this->filePath)); 115 | } 116 | 117 | return true; 118 | } 119 | } -------------------------------------------------------------------------------- /default/admin/modules/errors/list.php: -------------------------------------------------------------------------------- 1 | %s', $arg); 22 | break; 23 | case 'string': 24 | $args[] = sprintf('"%s"', $arg); 25 | break; 26 | case 'object': 27 | $args[] = sprintf('%s', get_class($arg)); 28 | break; 29 | case 'NULL': 30 | $args[] = 'NULL'; 31 | break; 32 | case 'boolean': 33 | $args[] = sprintf('%s', $arg ? 'true' : 'false'); 34 | break; 35 | case 'integer': 36 | case 'double': 37 | $args[] = sprintf('%s', $arg); 38 | break; 39 | case 'array': 40 | $args[] = sprintf('[ %s ]', implode(', ', build($arg))); 41 | break; 42 | default: 43 | $args[] = $type; 44 | break; 45 | } 46 | } 47 | 48 | return $args; 49 | } 50 | ?> 51 |
52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | $entry) { 63 | ?> 64 | 65 | 70 | 73 | 76 | 81 | 82 | 83 | 120 | 121 | 124 | 125 |
66 | 69 | 71 | getModule(); ?> 72 | 74 | getCode(); ?> 75 | 77 | 80 |
84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | getTrace() as $line) { 94 | ?> 95 | 96 | 106 | 113 | 114 | 117 | 118 |
97 | 105 | 107 | 112 |
119 |
126 |
-------------------------------------------------------------------------------- /default/js/codemirror/src/languages/config.js: -------------------------------------------------------------------------------- 1 | var words = {}; 2 | 3 | function define(style, dict) { 4 | for(var i = 0; i < dict.length; i++) { 5 | words[dict[i]] = style; 6 | } 7 | 8 | /* Resorting */ 9 | var temporary = Object.keys(words); 10 | var sorted = {}; 11 | temporary.sort(); 12 | temporary.reverse(); 13 | 14 | temporary.forEach(function(index) { 15 | sorted[index] = words[index]; 16 | }); 17 | 18 | words = sorted; 19 | }; 20 | 21 | var commonAtoms = [ 22 | /* Apache2 */ 23 | "All", "None", "Prod", 24 | 25 | /* ProFTPD: SQL */ 26 | "Backend", "Crypt", "Empty", "OpenSSL", "Plaintext", 27 | "mysql", 28 | "Argon2", "Bcrypt", "MD5", "PBKDF2", "Scrypt", "SHA1", "SHA256", "SHA512", 29 | "custom:/", "sql:/" 30 | ]; 31 | 32 | var commonKeywords = [ 33 | /* Apache2 */ 34 | "DocumentRoot", "DirectoryIndex", "AddDefaultCharset", "ErrorDocument", "Order", "Require", "AllowOverride", "Allow", "Options", 35 | "FollowSymLinks", "Indexes", "ServerAdmin", "ServerSignature", "ServerTokens", "Header", "IncludeOptional", "Server", "ErrorLog", 36 | "CustomLog", "Alias", 37 | 38 | /* ProFTPD */ 39 | "Include", "UseIPv6", "IdentLookups", "ServerName", "ServerType", "DeferWelcome", "DefaultServer", "ShowSymlinks", "TimeoutNoTransfer", "TimeoutStalled", 40 | "TimeoutIdle", "DisplayLogin", "DisplayChdir", "ListOptions", "DenyFilter", "DefaultRoot", "Port", "MaxInstances", "User", "Group", "Umask", "AllowOverwrite", 41 | "TransferLog", "SystemLog", "Ratios", "DelayEngine", "ControlsEngine", "ControlsMaxClients", "ControlsLog", "ControlsInterval", "ControlsSocket", "AdminControlsEngine", 42 | "RequireValidShell", "PassivePorts", "MasqueradeAddress", "DynMasqRefresh", "PersistentPasswd", "AuthOrder", "UseSendFile", "UseLastlog", "SetEnv", "QuotaEngine", 43 | "UserAlias", "DirFakeUser", "DirFakeGroup", "RequireValidShell", "MaxClients", "DisplayLogin", "DisplayChdir", "DenyAll", "AllowAll", "ModulePath", "ModuleControlsACLs", 44 | "LoadModule", 45 | 46 | /* ProFTPD: SQL */ 47 | "SQLBackend", "SQLEngine", "SQLAuthenticate", "SQLAuthTypes", "SQLPasswordEngine", "SQLConnectInfo", "SQLNamedQuery", "SQLUserInfo", "SQLMinUserUID", "SQLDefaultUID", 48 | "SQLMinUserGID", "SQLMinID", "CreateHome", "SQLLogFile", "SQLGroupInfo", "QuotaDirectoryTally", "QuotaDisplayUnits", "QuotaShowQuotas", "QuotaLimitTable", "QuotaTallyTable", 49 | "RootLogin" 50 | ]; 51 | 52 | var commonCommands = [ 53 | /* Apache2 */ 54 | "on", "off", "true", "false", 55 | 56 | "from", "allow", "all", "deny", ",deny", "granted", "denied", "user", "nogroup", "users", "groups", 57 | "set", "standalone", "anonymous", "common", "combined" 58 | ]; 59 | 60 | define('atom', commonAtoms); 61 | define('property', commonKeywords); 62 | define('builtin', commonCommands); 63 | 64 | function parse(stream, state) { 65 | var ch = stream.peek(); 66 | 67 | /* Comment */ 68 | if(ch == "#") { 69 | stream.skipToEnd(); 70 | return "comment"; 71 | } 72 | 73 | /* Variables */ 74 | if(stream.match(/^\$\w+/)) { 75 | return "def"; 76 | } 77 | 78 | /* Words */ 79 | for(let word in words) { 80 | if(stream.match(new RegExp('^(\\+|\\-)' + word + '', 'i')) || stream.match(new RegExp('^(' + word + ')')) || stream.match(new RegExp('^(' + word + ')$'))) { 81 | return words[word]; 82 | } 83 | } 84 | 85 | /* Numbers */ 86 | if(stream.match(/^(\d+)/)) { 87 | return "number"; 88 | } 89 | 90 | /* Strings */ 91 | if(stream.match(/^(".+")/)) { 92 | return "string"; 93 | } 94 | 95 | /* Groups */ 96 | if(stream.match(/^(<.+>)/)) { 97 | return "atom"; 98 | } 99 | 100 | /* Filesystem */ 101 | if(stream.match(/([\.\/a-zA-Z0-9_\-\*]+)\.([a-zA-Z0-9\*\-]+){1,6}/)) { 102 | return "operator"; 103 | } 104 | 105 | 106 | stream.next(); 107 | return null; 108 | } 109 | 110 | export const config = { 111 | name: "config", 112 | startState: function startState() { 113 | return { 114 | controlFlow: false, 115 | macroParameters: false, 116 | section: false 117 | }; 118 | }, 119 | token: parse, 120 | languageData: { 121 | autocomplete: commonAtoms.concat(commonKeywords, commonCommands), 122 | closeBrackets: { 123 | brackets: [ "<", "(", "[", "{", "'", '"', "`" ] 124 | }, 125 | commentTokens: { 126 | line: "#" 127 | } 128 | } 129 | }; -------------------------------------------------------------------------------- /libraries/PHPMailer/OAuth.php: -------------------------------------------------------------------------------- 1 | 9 | * @author Jim Jagielski (jimjag) 10 | * @author Andy Prevost (codeworxtech) 11 | * @author Brent R. Matzelle (original founder) 12 | * @copyright 2012 - 2015 Marcus Bointon 13 | * @copyright 2010 - 2012 Jim Jagielski 14 | * @copyright 2004 - 2009 Andy Prevost 15 | * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License 16 | * @note This program is distributed in the hope that it will be useful - WITHOUT 17 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 18 | * FITNESS FOR A PARTICULAR PURPOSE. 19 | */ 20 | 21 | namespace PHPMailer; 22 | 23 | use League\OAuth2\Client\Grant\RefreshToken; 24 | use League\OAuth2\Client\Provider\AbstractProvider; 25 | use League\OAuth2\Client\Token\AccessToken; 26 | 27 | /** 28 | * OAuth - OAuth2 authentication wrapper class. 29 | * Uses the oauth2-client package from the League of Extraordinary Packages. 30 | * 31 | * @see http://oauth2-client.thephpleague.com 32 | * 33 | * @author Marcus Bointon (Synchro/coolbru) 34 | */ 35 | class OAuth 36 | { 37 | /** 38 | * An instance of the League OAuth Client Provider. 39 | * 40 | * @var AbstractProvider 41 | */ 42 | protected $provider; 43 | 44 | /** 45 | * The current OAuth access token. 46 | * 47 | * @var AccessToken 48 | */ 49 | protected $oauthToken; 50 | 51 | /** 52 | * The user's email address, usually used as the login ID 53 | * and also the from address when sending email. 54 | * 55 | * @var string 56 | */ 57 | protected $oauthUserEmail = ''; 58 | 59 | /** 60 | * The client secret, generated in the app definition of the service you're connecting to. 61 | * 62 | * @var string 63 | */ 64 | protected $oauthClientSecret = ''; 65 | 66 | /** 67 | * The client ID, generated in the app definition of the service you're connecting to. 68 | * 69 | * @var string 70 | */ 71 | protected $oauthClientId = ''; 72 | 73 | /** 74 | * The refresh token, used to obtain new AccessTokens. 75 | * 76 | * @var string 77 | */ 78 | protected $oauthRefreshToken = ''; 79 | 80 | /** 81 | * OAuth constructor. 82 | * 83 | * @param array $options Associative array containing 84 | * `provider`, `userName`, `clientSecret`, `clientId` and `refreshToken` elements 85 | */ 86 | public function __construct($options) 87 | { 88 | $this->provider = $options['provider']; 89 | $this->oauthUserEmail = $options['userName']; 90 | $this->oauthClientSecret = $options['clientSecret']; 91 | $this->oauthClientId = $options['clientId']; 92 | $this->oauthRefreshToken = $options['refreshToken']; 93 | } 94 | 95 | /** 96 | * Get a new RefreshToken. 97 | * 98 | * @return RefreshToken 99 | */ 100 | protected function getGrant() 101 | { 102 | return new RefreshToken(); 103 | } 104 | 105 | /** 106 | * Get a new AccessToken. 107 | * 108 | * @return AccessToken 109 | */ 110 | protected function getToken() 111 | { 112 | return $this->provider->getAccessToken( 113 | $this->getGrant(), 114 | ['refresh_token' => $this->oauthRefreshToken] 115 | ); 116 | } 117 | 118 | /** 119 | * Generate a base64-encoded OAuth token. 120 | * 121 | * @return string 122 | */ 123 | public function getOauth64() 124 | { 125 | // Get a new token if it's not available or has expired 126 | if (null === $this->oauthToken or $this->oauthToken->hasExpired()) { 127 | $this->oauthToken = $this->getToken(); 128 | } 129 | 130 | return base64_encode( 131 | 'user=' . 132 | $this->oauthUserEmail . 133 | "\001auth=Bearer " . 134 | $this->oauthToken . 135 | "\001\001" 136 | ); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /default/module.php: -------------------------------------------------------------------------------- 1 | header(); 14 | 15 | if(method_exists($module->getInstance(), 'frame') && !empty($module->getInstance()->frame())) { 16 | ?> 17 | 18 | 21 |
22 |
34 | 50 | getCore()->getHooks()->applyFilter('buttons', []); 52 | 53 | if(!empty($buttons)) { 54 | ?> 55 |
56 | '); 60 | 61 | foreach($button as $entry) { 62 | if($entry instanceof Button) { 63 | if($entry->hasModal()) { 64 | printf('', $entry->getName(), $entry->getLabel(), $entry->getClasses(false), $entry->getModal()); 65 | } else { 66 | printf('', $entry->getName(), $entry->getLabel(), $entry->getClasses(false)); 67 | } 68 | } 69 | } 70 | 71 | printf('
'); 72 | } else { 73 | if($button instanceof Button) { 74 | if($button->hasModal()) { 75 | printf('', $button->getName(), $button->getLabel(), $button->getClasses(false), $button->getModal()); 76 | } else { 77 | printf('', $button->getName(), $button->getLabel(), $button->getClasses(false)); 78 | } 79 | } 80 | } 81 | } 82 | ?> 83 | 84 | 87 |
88 | 91 | 92 | 95 | 96 | getInstance()->content($submodule); 100 | ?> 101 |
102 | footer(); 106 | ?> -------------------------------------------------------------------------------- /default/js/statistics.js: -------------------------------------------------------------------------------- 1 | /** 2 | * fruithost | OpenSource Hosting 3 | * 4 | * @author Adrian Preuß 5 | * @version 1.0.0 6 | * @license MIT 7 | */ 8 | 9 | const Statistics = (function Statistics() { 10 | this.element = null; 11 | this.canvas = null; 12 | this.context = null; 13 | this.stopped = false; 14 | this.interval = null; 15 | this.position = 0; 16 | this.data = [0.00]; 17 | this.grid = [0, 0, 0, 0.5]; 18 | this.colors = [[0, 0, 0, 0]]; 19 | 20 | this.__constructor = function __constructor(element) { 21 | this.element = document.querySelector(element); 22 | this.canvas = document.createElement('canvas'); 23 | this.context = this.canvas.getContext('2d'); 24 | 25 | window.addEventListener("resize", this.resize.bind(this)); 26 | this.resize(); 27 | }; 28 | 29 | this.setColor = function setColor(color) { 30 | this.grid = color; 31 | }; 32 | 33 | this.start = function start() { 34 | this.stopped = false; 35 | }; 36 | 37 | this.stop = function stop() { 38 | this.stopped = true; 39 | }; 40 | 41 | this.resize = function resize() { 42 | this.canvas.width = (this.element.parentNode.innerWidth || this.element.parentNode.clientWidth || this.element.parentNode.clientWidth); 43 | this.canvas.height = 100; 44 | 45 | this.draw(); 46 | }; 47 | 48 | this.add = function add(value, color) { 49 | this.colors.push(color); 50 | this.data.push(value); 51 | }; 52 | 53 | this.draw = function draw() { 54 | this.context.save(); 55 | this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); 56 | this.context.restore(); 57 | 58 | let bw = this.canvas.width - this.position; 59 | let bh = this.canvas.height; 60 | let c = 4; 61 | let d = 1; 62 | 63 | /* Vertical Lines */ 64 | this.context.save(); 65 | this.context.fillStyle = 'rgba(' + this.grid.join(',') + ')'; 66 | this.context.strokeStyle = 'rgba(' + this.grid.join(',') + ')'; 67 | this.context.beginPath(); 68 | 69 | for (let x = 0; x <= bw; x += 40) { 70 | this.context.moveTo(x + this.position, c); 71 | this.context.lineTo(x + this.position, bh + c); 72 | } 73 | 74 | this.context.closePath(); 75 | this.context.stroke(); 76 | this.context.fill(); 77 | this.context.restore(); 78 | 79 | /* Horizontal Lines */ 80 | this.context.save(); 81 | this.context.fillStyle = 'rgba(' + this.grid.join(',') + ')'; 82 | this.context.strokeStyle = 'rgba(' + this.grid.join(',') + ')'; 83 | this.context.beginPath(); 84 | 85 | for (let x = 0; x <= bh + d; x += (15 + d)) { 86 | this.context.moveTo(this.position, x + c); 87 | this.context.lineTo(bw + this.position, +x + c); 88 | } 89 | this.context.closePath(); 90 | this.context.fill(); 91 | this.context.stroke(); 92 | this.context.restore(); 93 | 94 | /* Data */ 95 | this.context.save(); 96 | this.context.beginPath(); 97 | 98 | for (let i = 1; i < this.data.length; i++) { 99 | const height = (this.canvas.height * this.data[i]) / 100; 100 | const width = 40 + c + d; 101 | const x = this.canvas.width - (i * width); 102 | const y = this.canvas.height - height; 103 | 104 | this.context.fillStyle = 'rgba(' + this.colors[i].join(',') + ')'; 105 | this.context.strokeStyle = 'rgba(' + this.colors[i].join(',') + ')'; 106 | this.context.fillRect(x, y, width, height); 107 | } 108 | 109 | this.context.closePath(); 110 | this.context.restore(); 111 | 112 | window.requestAnimationFrame(this.draw.bind(this)); 113 | }; 114 | 115 | this.render = function render(interval) { 116 | this.element.appendChild(this.canvas); 117 | 118 | setInterval(function () { 119 | if (this.stopped) { 120 | return; 121 | } 122 | 123 | this.position -= 5; 124 | }.bind(this), interval); 125 | 126 | this.draw(); 127 | }; 128 | 129 | this.__constructor.apply(this, arguments); 130 | }); --------------------------------------------------------------------------------