├── .gitattributes ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── BUG.yml │ └── HELP.yml ├── .gitignore ├── .htaccess ├── .version ├── LICENSE ├── README.md ├── classes ├── Accounting │ ├── Auth.class.php │ ├── AuthFactory.class.php │ ├── Session.class.php │ └── User.class.php ├── Hardware │ ├── Memory.class.php │ ├── NetworkAddress.class.php │ ├── NetworkFamily.enum.php │ ├── NetworkFlag.enum.php │ ├── NetworkInterface.class.php │ ├── NetworkInterfaces.class.php │ ├── NetworkState.enum.php │ ├── OperatingSystem.class.php │ └── PhysicalDrives.class.php ├── Installer │ ├── Installer.class.php │ ├── InstallerFactory.class.php │ └── Repository.class.php ├── Localization │ └── I18N.class.php ├── Modules │ ├── Installer.class.php │ ├── Module.class.php │ ├── ModuleAuthor.class.php │ ├── ModuleError.class.php │ ├── ModuleInfo.class.php │ ├── ModuleInterface.class.php │ └── Modules.class.php ├── Network │ ├── Request.class.php │ ├── RequestFactory.class.php │ ├── Response.class.php │ ├── ResponseFactory.class.php │ ├── Route.class.php │ └── Router.class.php ├── Security │ └── Encryption.class.php ├── Services │ └── PHP.class.php ├── Storage │ ├── Database.class.php │ ├── DatabaseFactory.class.php │ └── PropertiesCache.class.php ├── System │ ├── Core.class.php │ ├── CoreAdmin.class.php │ ├── HookParameters.class.php │ ├── Hooks.class.php │ ├── Loader.class.php │ ├── Update.class.php │ └── Utils.class.php ├── Templating │ ├── Template.class.php │ ├── TemplateDefaults.class.php │ ├── TemplateFiles.class.php │ ├── TemplateNavigation.class.php │ └── TemplateNavigationCategory.class.php └── UI │ ├── Button.class.php │ ├── Icon.class.php │ └── Modal.class.php ├── default ├── account.php ├── admin │ ├── modules.php │ ├── modules │ │ ├── empty.php │ │ ├── errors │ │ │ ├── empty.php │ │ │ └── list.php │ │ ├── info.php │ │ ├── install.php │ │ ├── installing.php │ │ ├── list.php │ │ └── repository │ │ │ ├── create.php │ │ │ ├── empty.php │ │ │ └── list.php │ ├── themes.php │ ├── themes │ │ └── empty.php │ ├── users.php │ └── users │ │ ├── create.php │ │ ├── edit.php │ │ ├── edit │ │ ├── account.php │ │ ├── password.php │ │ ├── permissions.php │ │ └── settings.php │ │ ├── empty.php │ │ └── list.php ├── assets │ └── systems │ │ ├── debian.svg │ │ └── ubuntu.svg ├── css │ ├── bootstrap │ │ ├── bootstrap-grid.css │ │ ├── bootstrap-grid.css.map │ │ ├── bootstrap-grid.min.css │ │ ├── bootstrap-grid.min.css.map │ │ ├── bootstrap-grid.rtl.css │ │ ├── bootstrap-grid.rtl.css.map │ │ ├── bootstrap-grid.rtl.min.css │ │ ├── bootstrap-grid.rtl.min.css.map │ │ ├── bootstrap-reboot.css │ │ ├── bootstrap-reboot.css.map │ │ ├── bootstrap-reboot.min.css │ │ ├── bootstrap-reboot.min.css.map │ │ ├── bootstrap-reboot.rtl.css │ │ ├── bootstrap-reboot.rtl.css.map │ │ ├── bootstrap-reboot.rtl.min.css │ │ ├── bootstrap-reboot.rtl.min.css.map │ │ ├── bootstrap-utilities.css │ │ ├── bootstrap-utilities.css.map │ │ ├── bootstrap-utilities.min.css │ │ ├── bootstrap-utilities.min.css.map │ │ ├── bootstrap-utilities.rtl.css │ │ ├── bootstrap-utilities.rtl.css.map │ │ ├── bootstrap-utilities.rtl.min.css │ │ ├── bootstrap-utilities.rtl.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ ├── bootstrap.min.css.map │ │ ├── bootstrap.rtl.css │ │ ├── bootstrap.rtl.css.map │ │ ├── bootstrap.rtl.min.css │ │ └── bootstrap.rtl.min.css.map │ ├── components │ │ ├── console.css │ │ ├── debug.css │ │ ├── file-tree.css │ │ ├── module-info.css │ │ └── themes.css │ ├── global.css │ ├── login.css │ └── style.css ├── email │ ├── 2fa-code.html │ ├── 2fa-code.txt │ ├── lost-password.html │ ├── lost-password.txt │ ├── new-password.html │ └── new-password.txt ├── error │ ├── 404.php │ ├── module.php │ ├── module_empty.php │ └── permissions.php ├── fonts │ ├── bootstrap-icons │ │ ├── bootstrap-icons.css │ │ ├── bootstrap-icons.woff │ │ └── bootstrap-icons.woff2 │ └── cascadia-mono │ │ ├── cascadia-mono.css │ │ └── cascadia-mono.woff2 ├── footer.php ├── functions.php ├── header.php ├── icons.json ├── js │ ├── ajax.js │ ├── bootstrap │ │ ├── bootstrap.bundle.js │ │ ├── bootstrap.bundle.js.map │ │ ├── bootstrap.bundle.min.js │ │ ├── bootstrap.bundle.min.js.map │ │ ├── bootstrap.esm.js │ │ ├── bootstrap.esm.js.map │ │ ├── bootstrap.esm.min.js │ │ ├── bootstrap.esm.min.js.map │ │ ├── bootstrap.js │ │ ├── bootstrap.js.map │ │ ├── bootstrap.min.js │ │ └── bootstrap.min.js.map │ ├── codemirror │ │ ├── README.md │ │ ├── build │ │ │ ├── bundle.min.js │ │ │ ├── bundle.min.js.map │ │ │ └── bundle.src.js │ │ ├── package.json │ │ ├── rollup.config.mjs │ │ └── src │ │ │ ├── index.html │ │ │ ├── languages │ │ │ └── config.js │ │ │ ├── main.js │ │ │ └── theme.js │ ├── components │ │ ├── ajax.js │ │ ├── confirmation.js │ │ ├── console.js │ │ ├── loading.js │ │ ├── modal.js │ │ ├── module-info.js │ │ ├── popover.js │ │ ├── themes.js │ │ └── tooltip.js │ ├── jquery-ui │ │ ├── AUTHORS.txt │ │ ├── LICENSE.txt │ │ ├── external │ │ │ └── jquery │ │ │ │ └── jquery.js │ │ ├── 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 │ │ ├── index.html │ │ ├── jquery-ui.css │ │ ├── jquery-ui.js │ │ ├── jquery-ui.min.css │ │ ├── jquery-ui.min.js │ │ ├── jquery-ui.structure.css │ │ ├── jquery-ui.structure.min.css │ │ ├── jquery-ui.theme.css │ │ ├── jquery-ui.theme.min.css │ │ └── package.json │ ├── jquery │ │ ├── jquery-3.3.1.min.js │ │ └── jquery-3.3.1.slim.min.js │ ├── navigation.js │ ├── popper │ │ └── popper.min.js │ ├── statistics.js │ └── terminal.js ├── login.php ├── lost-password.php ├── modal.php ├── module.php ├── overview.php ├── server │ ├── console.php │ ├── logs.php │ ├── network.php │ ├── packages.php │ ├── reboot.php │ ├── server.php │ ├── services.php │ └── settings.php └── settings.php ├── favicon.gif ├── favicon.ico ├── handler ├── account.php ├── admin │ ├── modules.php │ └── users.php ├── login.php ├── lost-password.php ├── server │ ├── console.php │ ├── logs.php │ ├── network.php │ ├── reboot.php │ ├── server.php │ ├── services.modal.php │ ├── services.php │ └── settings.php ├── settings.php └── settings │ ├── default.php │ └── security.php ├── index.php ├── languages ├── $code.template ├── cs_CZ.po ├── de_DE.po ├── el_GR.po ├── es_ES.po ├── fr_FR.po ├── he_HE.po ├── hi_IN.po ├── index.php ├── it_IT.po ├── ja_JP.po ├── ko_KR.po ├── nl_NL.po ├── pl_PL.po ├── ru_RU.po ├── th_TH.po └── tr_TR.po ├── libraries ├── Markdown │ ├── LICENSE.txt │ ├── Parsedown.php │ ├── README.md │ └── composer.json ├── PHPMailer │ ├── Exception.php │ ├── OAuth.php │ ├── PHPMailer.php │ ├── POP3.php │ └── SMTP.php ├── Parsedown │ ├── LICENSE.txt │ ├── Parsedown.php │ ├── README.md │ └── composer.json ├── Sepia │ └── PoParser │ │ ├── Catalog │ │ ├── Catalog.php │ │ ├── CatalogArray.php │ │ ├── Entry.php │ │ ├── EntryFactory.php │ │ └── Header.php │ │ ├── Exception │ │ └── ParseException.php │ │ ├── Parser.php │ │ ├── PoCompiler.php │ │ └── SourceHandler │ │ ├── FileSystem.php │ │ ├── SourceHandler.php │ │ └── StringSource.php └── skoerfgen │ ├── ACMECert.php │ ├── LICENSE.md │ ├── README.md │ ├── alpn_responder.js │ ├── composer.json │ └── src │ ├── ACMECert.php │ ├── ACME_Exception.php │ └── ACMEv2.php └── robots.txt /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | [fork]: /fork 4 | [pr]: /compare 5 | [style]: https://fruithost.de/guidelines/styling 6 | [code-of-conduct]: .github/CODE_OF_CONDUCT.md 7 | 8 | Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great. 9 | 10 | Please note that this project is released with a [Contributor Code of Conduct][code-of-conduct]. By participating in this project you agree to abide by its terms. 11 | 12 | ## Issues and PRs 13 | 14 | If you have suggestions for how this project could be improved, or want to report a bug, open an issue! We'd love all and any contributions. If you have questions, too, we'd love to hear them. 15 | 16 | We'd also love PRs. If you're thinking of a large PR, we advise opening up an issue first to talk about it, though! Look at the links below if you're not sure how to open a PR. 17 | 18 | ## Submitting a pull request 19 | 20 | 1. [Fork][fork] and clone the repository. 21 | 1. Create a new branch: `git checkout -b my-branch-name`. 22 | 1. Make your change, add tests, and make sure that you dont have any errors. 23 | 1. Push to your fork and [submit a pull request][pr]. 24 | 1. Pat your self on the back and wait for your pull request to be reviewed and merged. 25 | 26 | Here are a few things you can do that will increase the likelihood of your pull request being accepted: 27 | 28 | - Follow the [style guide][style] which is using standard. Any linting errors should be shown when running `npm test`. 29 | - Write and update tests. 30 | - Keep your changes as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests. 31 | - Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). 32 | 33 | Work in Progress pull requests are also welcome to get feedback early on, or if there is something blocked you. 34 | 35 | ## Resources 36 | 37 | - [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) 38 | - [Using Pull Requests](https://help.github.com/articles/about-pull-requests/) 39 | - [GitHub Help](https://help.github.com) 40 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: fruithost 2 | patreon: fruithost 3 | custom: ['https://paypal.me/debitdirect'] 4 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .security.php 2 | .mail.php 3 | .config.php 4 | .DEBUG 5 | ~DEBUG 6 | .DEMO 7 | ~DEMO 8 | .fruithost 9 | .idea/* 10 | temp/* 11 | default/js/codemirror/node_modules/ 12 | default/js/codemirror/package-lock.json 13 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.version: -------------------------------------------------------------------------------- 1 | 1.0.4 -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /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/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 | ?> -------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /classes/Installer/InstallerFactory.class.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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/Modules/Installer.class.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /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/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 | ?> -------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /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/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 | ?> -------------------------------------------------------------------------------- /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/Security/Encryption.class.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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/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 | ?> -------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /classes/UI/Modal.class.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 | ?> -------------------------------------------------------------------------------- /default/admin/modules/empty.php: -------------------------------------------------------------------------------- 1 | 13 |
14 | 15 |

16 |

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

16 |

17 |
-------------------------------------------------------------------------------- /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/admin/modules/info.php: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /default/admin/modules/installing.php: -------------------------------------------------------------------------------- 1 | 10 |
11 |
12 | Loading... 13 |
14 |
15 | 16 | -------------------------------------------------------------------------------- /default/admin/modules/repository/empty.php: -------------------------------------------------------------------------------- 1 | 13 |
14 | 15 |

16 |

17 | 18 |
-------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /default/admin/themes/empty.php: -------------------------------------------------------------------------------- 1 | 13 |
14 | 15 |

16 |

17 |
-------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /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/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/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 |
-------------------------------------------------------------------------------- /default/admin/users/empty.php: -------------------------------------------------------------------------------- 1 | 13 |
14 | 15 |

16 |

17 |
-------------------------------------------------------------------------------- /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 |
-------------------------------------------------------------------------------- /default/assets/systems/ubuntu.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /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/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/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 | } -------------------------------------------------------------------------------- /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/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 | } -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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/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/email/lost-password.txt: -------------------------------------------------------------------------------- 1 | Hey $USERNAME, 2 | 3 | We've received a request to reset your password. if you didn't make the request, just ignore this email. Otherwise, you can reset your password using this link: 4 | 5 | $LINK 6 | 7 | Thanks, 8 | 9 | The fruithost Team -------------------------------------------------------------------------------- /default/email/new-password.txt: -------------------------------------------------------------------------------- 1 | Hey $USERNAME, 2 | 3 | We've reset your password. Here is your new Account-Password: 4 | $PASSWORD 5 | 6 | Thanks, 7 | 8 | The fruithost Team -------------------------------------------------------------------------------- /default/error/module.php: -------------------------------------------------------------------------------- 1 | header(); 14 | ?> 15 | 36 |
37 | 38 |

39 |

40 |
41 | footer(); 43 | ?> -------------------------------------------------------------------------------- /default/error/module_empty.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 | ?> -------------------------------------------------------------------------------- /default/fonts/bootstrap-icons/bootstrap-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fruithost/Panel/7bf14dfdbfd0aaa42ff48758f5a2a345520869ff/default/fonts/bootstrap-icons/bootstrap-icons.woff -------------------------------------------------------------------------------- /default/fonts/bootstrap-icons/bootstrap-icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fruithost/Panel/7bf14dfdbfd0aaa42ff48758f5a2a345520869ff/default/fonts/bootstrap-icons/bootstrap-icons.woff2 -------------------------------------------------------------------------------- /default/fonts/cascadia-mono/cascadia-mono.css: -------------------------------------------------------------------------------- 1 | /* fallback */ 2 | @font-face { 3 | font-family: 'Cascadia Mono'; 4 | font-style: normal; 5 | font-weight: 400; 6 | src: url('cascadia-mono.woff2') format('woff2'); 7 | } -------------------------------------------------------------------------------- /default/fonts/cascadia-mono/cascadia-mono.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fruithost/Panel/7bf14dfdbfd0aaa42ff48758f5a2a345520869ff/default/fonts/cascadia-mono/cascadia-mono.woff2 -------------------------------------------------------------------------------- /default/footer.php: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | 18 | 19 | foot(); 23 | ?> 24 | 25 | -------------------------------------------------------------------------------- /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/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 | } -------------------------------------------------------------------------------- /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 | }; -------------------------------------------------------------------------------- /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`. -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/codemirror/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | CodeMirror 6 7 | 9 | 10 | 11 |
12 | 13 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /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 | }; -------------------------------------------------------------------------------- /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 | }); -------------------------------------------------------------------------------- /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 | })(); -------------------------------------------------------------------------------- /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/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/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 | }); -------------------------------------------------------------------------------- /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 | }); -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /default/js/jquery-ui/images/ui-icons_444444_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fruithost/Panel/7bf14dfdbfd0aaa42ff48758f5a2a345520869ff/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/7bf14dfdbfd0aaa42ff48758f5a2a345520869ff/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/7bf14dfdbfd0aaa42ff48758f5a2a345520869ff/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/7bf14dfdbfd0aaa42ff48758f5a2a345520869ff/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/7bf14dfdbfd0aaa42ff48758f5a2a345520869ff/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/7bf14dfdbfd0aaa42ff48758f5a2a345520869ff/default/js/jquery-ui/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /default/js/jquery-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-ui", 3 | "title": "jQuery UI", 4 | "description": "A curated set of user interface interactions, effects, widgets, and themes built on top of the jQuery JavaScript Library.", 5 | "version": "1.12.1", 6 | "homepage": "http://jqueryui.com", 7 | "author": { 8 | "name": "jQuery Foundation and other contributors", 9 | "url": "https://github.com/jquery/jquery-ui/blob/1.12.1/AUTHORS.txt" 10 | }, 11 | "main": "ui/widget.js", 12 | "maintainers": [ 13 | { 14 | "name": "Scott González", 15 | "email": "scott.gonzalez@gmail.com", 16 | "url": "http://scottgonzalez.com" 17 | }, 18 | { 19 | "name": "Jörn Zaefferer", 20 | "email": "joern.zaefferer@gmail.com", 21 | "url": "http://bassistance.de" 22 | }, 23 | { 24 | "name": "Mike Sherov", 25 | "email": "mike.sherov@gmail.com", 26 | "url": "http://mike.sherov.com" 27 | }, 28 | { 29 | "name": "TJ VanToll", 30 | "email": "tj.vantoll@gmail.com", 31 | "url": "http://tjvantoll.com" 32 | }, 33 | { 34 | "name": "Felix Nagel", 35 | "email": "info@felixnagel.com", 36 | "url": "http://www.felixnagel.com" 37 | }, 38 | { 39 | "name": "Alex Schmitz", 40 | "email": "arschmitz@gmail.com", 41 | "url": "https://github.com/arschmitz" 42 | } 43 | ], 44 | "repository": { 45 | "type": "git", 46 | "url": "git://github.com/jquery/jquery-ui.git" 47 | }, 48 | "bugs": "https://bugs.jqueryui.com/", 49 | "license": "MIT", 50 | "scripts": { 51 | "test": "grunt" 52 | }, 53 | "dependencies": {}, 54 | "devDependencies": { 55 | "commitplease": "2.3.0", 56 | "grunt": "0.4.5", 57 | "grunt-bowercopy": "1.2.4", 58 | "grunt-cli": "0.1.13", 59 | "grunt-compare-size": "0.4.0", 60 | "grunt-contrib-concat": "0.5.1", 61 | "grunt-contrib-csslint": "0.5.0", 62 | "grunt-contrib-jshint": "0.12.0", 63 | "grunt-contrib-qunit": "1.0.1", 64 | "grunt-contrib-requirejs": "0.4.4", 65 | "grunt-contrib-uglify": "0.11.1", 66 | "grunt-git-authors": "3.1.0", 67 | "grunt-html": "6.0.0", 68 | "grunt-jscs": "2.1.0", 69 | "load-grunt-tasks": "3.4.0", 70 | "rimraf": "2.5.1", 71 | "testswarm": "1.1.0" 72 | }, 73 | "keywords": [] 74 | } 75 | -------------------------------------------------------------------------------- /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/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 | }); -------------------------------------------------------------------------------- /default/modal.php: -------------------------------------------------------------------------------- 1 | 0) { 11 | foreach($modals AS $modal) { 12 | ?> 13 | 53 | isShowing()) { 55 | ?> 56 | 61 | -------------------------------------------------------------------------------- /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/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/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/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 | ?> -------------------------------------------------------------------------------- /favicon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fruithost/Panel/7bf14dfdbfd0aaa42ff48758f5a2a345520869ff/favicon.gif -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fruithost/Panel/7bf14dfdbfd0aaa42ff48758f5a2a345520869ff/favicon.ico -------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /handler/server/services.modal.php: -------------------------------------------------------------------------------- 1 | 10 |
11 | 	
14 | 
-------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /handler/settings.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /languages/index.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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/Markdown/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /libraries/Sepia/PoParser/Catalog/Catalog.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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /libraries/Sepia/PoParser/Exception/ParseException.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 | } -------------------------------------------------------------------------------- /libraries/Sepia/PoParser/SourceHandler/SourceHandler.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 | -------------------------------------------------------------------------------- /libraries/skoerfgen/ACMECert.php: -------------------------------------------------------------------------------- 1 | =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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: --------------------------------------------------------------------------------